2020-09-12 12:11:14 -07:00
|
|
|
// @ts-check
|
|
|
|
|
2024-11-28 20:36:44 -08:00
|
|
|
import fs from "node:fs/promises";
|
|
|
|
import path from "node:path";
|
|
|
|
import test from "ava";
|
2024-12-03 19:58:28 -08:00
|
|
|
import { lint } from "markdownlint/promise";
|
|
|
|
import { applyFixes } from "markdownlint";
|
2024-11-28 20:36:44 -08:00
|
|
|
import helpers from "../helpers/helpers.cjs";
|
|
|
|
import { fixableRuleNames } from "../lib/constants.mjs";
|
2020-09-12 12:11:14 -07:00
|
|
|
|
2023-07-21 22:49:08 -07:00
|
|
|
const numericalSortCompareFn = (a, b) => a - b;
|
|
|
|
|
2020-09-12 12:11:14 -07:00
|
|
|
/**
|
|
|
|
* Create a test function for the specified test file.
|
|
|
|
*
|
|
|
|
* @param {string} file Test file relative path.
|
2023-09-04 21:41:16 -07:00
|
|
|
* @returns {import("ava").Implementation<unknown[]>} Test function.
|
2020-09-12 12:11:14 -07:00
|
|
|
*/
|
|
|
|
function createTestForFile(file) {
|
2023-03-15 21:26:22 -07:00
|
|
|
return (t) => (
|
|
|
|
// Read and lint Markdown test file
|
|
|
|
Promise.all([
|
|
|
|
fs.readFile(file, "utf8"),
|
2024-12-03 19:58:28 -08:00
|
|
|
lint({
|
2023-03-15 21:26:22 -07:00
|
|
|
"files": [ file ]
|
|
|
|
})
|
|
|
|
])
|
2022-05-10 06:11:46 +00:00
|
|
|
// Compare expected results and snapshot
|
|
|
|
.then((params) => {
|
2023-03-15 21:26:22 -07:00
|
|
|
const [ content, results ] = params;
|
2022-05-10 06:11:46 +00:00
|
|
|
// Canonicalize version number
|
2022-06-08 22:10:27 -07:00
|
|
|
const errors = results[file]
|
|
|
|
.filter((error) => !!error.ruleInformation);
|
|
|
|
for (const error of errors) {
|
|
|
|
error.ruleInformation =
|
2022-05-10 06:11:46 +00:00
|
|
|
error.ruleInformation.replace(/v\d+\.\d+\.\d+/, "v0.0.0");
|
2022-06-08 22:10:27 -07:00
|
|
|
}
|
2022-05-10 06:11:46 +00:00
|
|
|
// Match identified issues by MD### markers
|
2023-12-28 16:46:15 -08:00
|
|
|
const marker = /\{(MD\d+)(?::([-+]?)(\d+))?\}/g;
|
2022-05-10 06:11:46 +00:00
|
|
|
const lines = content.split(helpers.newLineRe);
|
|
|
|
const expected = {};
|
2022-06-08 22:10:27 -07:00
|
|
|
// @ts-ignore
|
|
|
|
for (const [ index, line ] of lines.entries()) {
|
2022-05-10 06:11:46 +00:00
|
|
|
let match = null;
|
|
|
|
while ((match = marker.exec(line))) {
|
2023-12-28 16:46:15 -08:00
|
|
|
const [ , rule, delta, value ] = match;
|
|
|
|
let lineNumber = index + 1;
|
|
|
|
if (value) {
|
|
|
|
const valueInt = Number.parseInt(value, 10);
|
|
|
|
if (delta) {
|
|
|
|
lineNumber += ((delta === "+") ? 1 : -1) * valueInt;
|
|
|
|
} else {
|
|
|
|
lineNumber = valueInt;
|
|
|
|
}
|
|
|
|
}
|
2022-05-10 06:11:46 +00:00
|
|
|
// eslint-disable-next-line no-multi-assign
|
|
|
|
const indices = expected[rule] = expected[rule] || [];
|
2023-03-15 21:26:22 -07:00
|
|
|
if (!indices.includes(lineNumber)) {
|
|
|
|
indices.push(lineNumber);
|
|
|
|
}
|
2022-05-10 06:11:46 +00:00
|
|
|
}
|
2022-06-08 22:10:27 -07:00
|
|
|
}
|
2023-05-30 20:14:02 -07:00
|
|
|
for (const list of Object.values(expected)) {
|
2023-07-21 22:49:08 -07:00
|
|
|
list.sort(numericalSortCompareFn);
|
2023-05-30 20:14:02 -07:00
|
|
|
}
|
2022-10-15 16:06:20 -07:00
|
|
|
const actual = {};
|
|
|
|
for (const error of errors) {
|
|
|
|
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);
|
2022-06-08 22:10:27 -07:00
|
|
|
}
|
2022-10-29 23:21:45 -07:00
|
|
|
t.true(
|
2024-11-28 20:36:44 -08:00
|
|
|
!error.fixInfo || fixableRuleNames.includes(rule),
|
2022-10-29 23:21:45 -07:00
|
|
|
`Fixable rule ${rule} is not tagged as such.`
|
|
|
|
);
|
2020-09-12 12:11:14 -07:00
|
|
|
}
|
2023-05-30 20:14:02 -07:00
|
|
|
for (const list of Object.values(actual)) {
|
2023-07-21 22:49:08 -07:00
|
|
|
list.sort(numericalSortCompareFn);
|
2023-05-30 20:14:02 -07:00
|
|
|
}
|
2022-10-15 16:06:20 -07:00
|
|
|
t.deepEqual(actual, expected, "Too few or too many issues found.");
|
2022-05-10 06:11:46 +00:00
|
|
|
// Create snapshot
|
2024-10-06 17:24:44 -07:00
|
|
|
const fixed = applyFixes(content, errors)
|
2022-05-10 06:11:46 +00:00
|
|
|
.replace(/\r\n/g, "\n");
|
|
|
|
t.snapshot({
|
|
|
|
errors,
|
|
|
|
fixed
|
|
|
|
});
|
2022-05-11 06:07:43 +00:00
|
|
|
// Identify missing fixes
|
2024-12-03 19:58:28 -08:00
|
|
|
return lint({
|
2022-05-10 06:11:46 +00:00
|
|
|
"strings": {
|
|
|
|
"input": fixed
|
2023-03-15 21:26:22 -07:00
|
|
|
}
|
2022-05-11 06:07:43 +00:00
|
|
|
}).then((fixedResults) => {
|
|
|
|
const unfixed = fixedResults.input.filter((error) => !!error.fixInfo);
|
2022-05-10 06:11:46 +00:00
|
|
|
t.deepEqual(unfixed, [], "Fixable error(s) not fixed.");
|
|
|
|
});
|
|
|
|
})
|
2020-09-12 12:11:14 -07:00
|
|
|
.catch()
|
2023-03-15 21:26:22 -07:00
|
|
|
);
|
2020-09-12 12:11:14 -07:00
|
|
|
}
|
|
|
|
|
2024-11-28 20:36:44 -08:00
|
|
|
const dir = await fs.readdir("./test");
|
|
|
|
const files = dir.filter((file) => /\.md$/.test(file));
|
2025-03-22 16:42:37 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Creates tests for chunk N/M of the set.
|
|
|
|
*
|
|
|
|
* @param {number} number Chunk number.
|
|
|
|
* @param {number} total Total chunks.
|
|
|
|
* @returns {void}
|
|
|
|
*/
|
|
|
|
export function createTests(number, total) {
|
|
|
|
const start = Math.floor(((number - 1) * files.length) / total);
|
|
|
|
const end = Math.floor((number * files.length) / total);
|
|
|
|
for (const file of files.slice(start, end)) {
|
|
|
|
test(file, createTestForFile(path.join("./test", file)));
|
|
|
|
}
|
2022-06-08 22:10:27 -07:00
|
|
|
}
|