markdownlint/test/markdownlint-test-config.mjs

594 lines
16 KiB
JavaScript
Raw Permalink Normal View History

// @ts-check
import fs from "node:fs";
import { createRequire } from "node:module";
const require = createRequire(import.meta.url);
import os from "node:os";
import path from "node:path";
import test from "ava";
import { readConfig as readConfigAsync } from "markdownlint/async";
import { extendConfig, readConfig as readConfigPromise } from "markdownlint/promise";
import { readConfig as readConfigSync } from "markdownlint/sync";
import { __dirname } from "./esm-helpers.mjs";
const sameFileSystem = (path.relative(os.homedir(), __dirname(import.meta)) !== __dirname(import.meta));
test("configSingle", (t) => new Promise((resolve) => {
t.plan(2);
readConfigAsync("./test/config/config-child.json",
function callback(err, actual) {
t.falsy(err);
const expected = require("./config/config-child.json");
t.deepEqual(actual, expected, "Config object not correct.");
resolve();
});
}));
test("configAbsolute", (t) => new Promise((resolve) => {
t.plan(2);
readConfigAsync(path.join(__dirname(import.meta), "config", "config-child.json"),
function callback(err, actual) {
t.falsy(err);
const expected = require("./config/config-child.json");
t.deepEqual(actual, expected, "Config object not correct.");
resolve();
});
}));
if (sameFileSystem) {
test("configTilde", (t) => new Promise((resolve) => {
t.plan(2);
readConfigAsync(
`~/${path.relative(os.homedir(), "./test/config/config-child.json")}`,
function callback(err, actual) {
t.falsy(err);
const expected = require("./config/config-child.json");
t.deepEqual(actual, expected, "Config object not correct.");
resolve();
});
}));
}
test("configMultiple", (t) => new Promise((resolve) => {
t.plan(2);
readConfigAsync("./test/config/config-grandparent.json",
function callback(err, actual) {
t.falsy(err);
const expected = {
...require("./config/config-child.json"),
...require("./config/config-parent.json"),
...require("./config/config-grandparent.json")
};
// @ts-ignore
delete expected.extends;
t.deepEqual(actual, expected, "Config object not correct.");
resolve();
});
}));
test("configMultipleWithRequireResolve", (t) => new Promise((resolve) => {
t.plan(2);
readConfigAsync("./test/config/config-packageparent.json",
function callback(err, actual) {
t.falsy(err);
const expected = {
...require("./node_modules/pseudo-package/config-frompackage.json"),
...require("./config/config-packageparent.json")
};
// @ts-ignore
delete expected.extends;
t.deepEqual(actual, expected, "Config object not correct.");
resolve();
});
}));
test("configCustomFileSystem", (t) => new Promise((resolve) => {
t.plan(3);
const file = "/dir/file.json";
const extended = "~/dir/extended.json";
const expanded = path.join(os.homedir(), extended.slice(1));
const fileContent = {
"extends": extended,
"default": true,
"MD001": false
};
const extendedContent = {
"MD001": true,
"MD041": true
};
const fsApi = {
"access": (p, m, cb) => {
t.is(p, expanded);
return (cb || m)();
},
"readFile": (p, o, cb) => {
if (p === file) {
return cb(null, JSON.stringify(fileContent));
} else if (p === expanded) {
return cb(null, JSON.stringify(extendedContent));
}
return t.fail(p);
}
};
readConfigAsync(
file,
// @ts-ignore
null,
fsApi,
function callback(err, actual) {
t.falsy(err);
const expected = {
...extendedContent,
...fileContent
};
// @ts-ignore
delete expected.extends;
t.deepEqual(actual, expected, "Config object not correct.");
resolve();
});
}));
test("configBadFile", (t) => new Promise((resolve) => {
t.plan(4);
readConfigAsync("./test/config/config-badfile.json",
function callback(err, result) {
t.truthy(err, "Did not get an error for bad file.");
t.true(err instanceof Error, "Error not instance of Error.");
// @ts-ignore
t.is(err.code, "ENOENT", "Error code for bad file not ENOENT.");
t.true(!result, "Got result for bad file.");
resolve();
});
}));
test("configBadChildFile", (t) => new Promise((resolve) => {
t.plan(4);
readConfigAsync("./test/config/config-badchildfile.json",
function callback(err, result) {
t.truthy(err, "Did not get an error for bad child file.");
t.true(err instanceof Error, "Error not instance of Error.");
// @ts-ignore
t.is(err.code, "ENOENT",
"Error code for bad child file not ENOENT.");
t.true(!result, "Got result for bad child file.");
resolve();
});
}));
test("configBadChildPackage", (t) => new Promise((resolve) => {
t.plan(4);
readConfigAsync("./test/config/config-badchildpackage.json",
function callback(err, result) {
t.truthy(err, "Did not get an error for bad child package.");
t.true(err instanceof Error, "Error not instance of Error.");
// @ts-ignore
t.is(err.code, "ENOENT",
"Error code for bad child package not ENOENT.");
t.true(!result, "Got result for bad child package.");
resolve();
});
}));
test("configBadJson", (t) => new Promise((resolve) => {
t.plan(3);
readConfigAsync("./test/config/config-badjson.json",
function callback(err, result) {
t.truthy(err, "Did not get an error for bad JSON.");
t.true(err instanceof Error, "Error not instance of Error.");
t.true(!result, "Got result for bad JSON.");
resolve();
});
}));
test("configBadChildJson", (t) => new Promise((resolve) => {
t.plan(3);
readConfigAsync("./test/config/config-badchildjson.json",
function callback(err, result) {
t.truthy(err, "Did not get an error for bad child JSON.");
t.true(err instanceof Error, "Error not instance of Error.");
t.true(!result, "Got result for bad child JSON.");
resolve();
});
}));
test("configSingleYaml", (t) => new Promise((resolve) => {
t.plan(2);
readConfigAsync(
"./test/config/config-child.yaml",
// @ts-ignore
[ require("js-yaml").load ],
function callback(err, actual) {
t.falsy(err);
const expected = require("./config/config-child.json");
t.deepEqual(actual, expected, "Config object not correct.");
resolve();
});
}));
test("configMultipleYaml", (t) => new Promise((resolve) => {
t.plan(2);
readConfigAsync(
"./test/config/config-grandparent.yaml",
// @ts-ignore
[ require("js-yaml").load ],
function callback(err, actual) {
t.falsy(err);
const expected = {
...require("./config/config-child.json"),
...require("./config/config-parent.json"),
...require("./config/config-grandparent.json")
};
// @ts-ignore
delete expected.extends;
t.deepEqual(actual, expected, "Config object not correct.");
resolve();
});
}));
test("configMultipleHybrid", (t) => new Promise((resolve) => {
t.plan(2);
readConfigAsync(
"./test/config/config-grandparent-hybrid.yaml",
// @ts-ignore
[ JSON.parse, require("toml").parse, require("js-yaml").load ],
function callback(err, actual) {
t.falsy(err);
const expected = {
...require("./config/config-child.json"),
...require("./config/config-parent.json"),
...require("./config/config-grandparent.json")
};
// @ts-ignore
delete expected.extends;
t.like(actual, expected, "Config object not correct.");
resolve();
});
}));
test("configBadHybrid", (t) => new Promise((resolve) => {
t.plan(4);
readConfigAsync(
"./test/config/config-badcontent.txt",
// @ts-ignore
[ JSON.parse, require("toml").parse, require("js-yaml").load ],
function callback(err, result) {
t.truthy(err, "Did not get an error for bad child JSON.");
t.true(err instanceof Error, "Error not instance of Error.");
t.truthy(err.message.match(
/^Unable to parse '[^']*'; Parser \d+: (Unexpected token|Expected property name)/
), "Error message unexpected.");
t.true(!result, "Got result for bad child JSON.");
resolve();
});
}));
test("configSingleSync", (t) => {
t.plan(1);
const actual = readConfigSync("./test/config/config-child.json");
const expected = require("./config/config-child.json");
t.deepEqual(actual, expected, "Config object not correct.");
});
test("configAbsoluteSync", (t) => {
t.plan(1);
const actual = readConfigSync(
path.join(__dirname(import.meta), "config", "config-child.json"));
const expected = require("./config/config-child.json");
t.deepEqual(actual, expected, "Config object not correct.");
});
if (sameFileSystem) {
test("configTildeSync", (t) => {
t.plan(1);
const actual = readConfigSync(
`~/${path.relative(os.homedir(), "./test/config/config-child.json")}`);
const expected = require("./config/config-child.json");
t.deepEqual(actual, expected, "Config object not correct.");
});
}
test("configMultipleSync", (t) => {
t.plan(1);
const actual =
readConfigSync("./test/config/config-grandparent.json");
const expected = {
...require("./config/config-child.json"),
...require("./config/config-parent.json"),
...require("./config/config-grandparent.json")
};
// @ts-ignore
delete expected.extends;
t.deepEqual(actual, expected, "Config object not correct.");
});
test("configBadFileSync", (t) => {
t.plan(1);
t.throws(
function badFileCall() {
readConfigSync("./test/config/config-badfile.json");
},
{
"message": /ENOENT/
},
"Did not get correct exception for bad file."
);
});
test("configBadChildFileSync", (t) => {
t.plan(1);
t.throws(
function badChildFileCall() {
readConfigSync("./test/config/config-badchildfile.json");
},
{
"message": /ENOENT/
},
"Did not get correct exception for bad child file."
);
});
test("configBadJsonSync", (t) => {
t.plan(1);
t.throws(
function badJsonCall() {
readConfigSync("./test/config/config-badjson.json");
},
{
"message":
/Unable to parse '[^']*'; Parser \d+: (Unexpected token|Expected property name)/
},
"Did not get correct exception for bad JSON."
);
});
test("configBadChildJsonSync", (t) => {
t.plan(1);
t.throws(
function badChildJsonCall() {
readConfigSync("./test/config/config-badchildjson.json");
},
{
"message":
/Unable to parse '[^']*'; Parser \d+: (Unexpected token|Expected property name)/
},
"Did not get correct exception for bad child JSON."
);
});
test("configSingleYamlSync", (t) => {
t.plan(1);
const actual = readConfigSync(
// @ts-ignore
"./test/config/config-child.yaml", [ require("js-yaml").load ]);
const expected = require("./config/config-child.json");
t.deepEqual(actual, expected, "Config object not correct.");
});
test("configMultipleYamlSync", (t) => {
t.plan(1);
const actual = readConfigSync(
// @ts-ignore
"./test/config/config-grandparent.yaml", [ require("js-yaml").load ]);
const expected = {
...require("./config/config-child.json"),
...require("./config/config-parent.json"),
...require("./config/config-grandparent.json")
};
// @ts-ignore
delete expected.extends;
t.deepEqual(actual, expected, "Config object not correct.");
});
test("configMultipleHybridSync", (t) => {
t.plan(1);
const actual = readConfigSync(
"./test/config/config-grandparent-hybrid.yaml",
// @ts-ignore
[ JSON.parse, require("toml").parse, require("js-yaml").load ]);
const expected = {
...require("./config/config-child.json"),
...require("./config/config-parent.json"),
...require("./config/config-grandparent.json")
};
// @ts-ignore
delete expected.extends;
t.like(actual, expected, "Config object not correct.");
});
test("configCustomFileSystemSync", (t) => {
t.plan(2);
const file = "/dir/file.json";
const extended = "~/dir/extended.json";
const expanded = path.join(os.homedir(), extended.slice(1));
const fileContent = {
"extends": extended,
"default": true,
"MD001": false
};
const extendedContent = {
"MD001": true,
"MD041": true
};
const fsApi = {
"accessSync": (p) => {
t.is(p, expanded);
},
"readFileSync": (p) => {
if (p === file) {
return JSON.stringify(fileContent);
} else if (p === expanded) {
return JSON.stringify(extendedContent);
}
return t.fail(p);
}
};
const actual = readConfigSync(file, undefined, fsApi);
const expected = {
...extendedContent,
...fileContent
};
// @ts-ignore
delete expected.extends;
t.deepEqual(actual, expected, "Config object not correct.");
});
test("configBadHybridSync", (t) => {
t.plan(1);
t.throws(
function badHybridCall() {
readConfigSync(
"./test/config/config-badcontent.txt",
// @ts-ignore
[ JSON.parse, require("toml").parse, require("js-yaml").load ]);
},
{
"message": /^Unable to parse '[^']*'; Parser \d+: (Unexpected token|Expected property name)/
},
"Did not get correct exception for bad content."
);
});
test("configSinglePromise", (t) => new Promise((resolve) => {
t.plan(1);
readConfigPromise("./test/config/config-child.json")
.then((actual) => {
const expected = require("./config/config-child.json");
t.deepEqual(actual, expected, "Config object not correct.");
resolve();
});
}));
test("configCustomFileSystemPromise", (t) => new Promise((resolve) => {
t.plan(4);
const file = path.resolve("/dir/file.json");
const extended = path.resolve("/dir/extended.json");
const fileContent = {
"extends": extended,
"default": true,
"MD001": false
};
const extendedContent = {
"MD001": true,
"MD041": true
};
const fsApi = {
"access": (p, m, cb) => {
t.is(p, extended);
return (cb || m)();
},
"readFile": (p, o, cb) => {
switch (p) {
case file:
t.is(p, file);
return cb(null, JSON.stringify(fileContent));
case extended:
t.is(p, extended);
return cb(null, JSON.stringify(extendedContent));
default:
return t.fail();
}
}
};
readConfigPromise(file, undefined, fsApi)
.then((actual) => {
const expected = {
...extendedContent,
...fileContent
};
// @ts-ignore
delete expected.extends;
t.deepEqual(actual, expected, "Config object not correct.");
resolve();
});
}));
test("configBadFilePromise", (t) => new Promise((resolve) => {
t.plan(2);
readConfigPromise("./test/config/config-badfile.json")
.then(
null,
(error) => {
t.truthy(error, "Did not get an error for bad JSON.");
t.true(error instanceof Error, "Error not instance of Error.");
resolve();
}
);
}));
test("extendSinglePromise", (t) => new Promise((resolve) => {
t.plan(1);
const expected = require("./config/config-child.json");
extendConfig(
expected,
"./test/config/config-child.json",
undefined,
fs
)
.then((actual) => {
t.deepEqual(actual, expected, "Config object not correct.");
resolve();
});
}));
test("extendBadPromise", (t) => new Promise((resolve) => {
t.plan(2);
extendConfig(
{
"extends": "missing.json"
},
"./test/config/missing.json",
undefined,
fs
)
.then(
null,
(error) => {
t.truthy(error, "Did not get an error for bad input.");
t.true(error instanceof Error, "Error not instance of Error.");
resolve();
}
);
}));
test("extendCustomFileSystemPromise", (t) => new Promise((resolve) => {
t.plan(4);
const file = path.resolve("/dir/file.json");
const extended = path.resolve("/dir/extended.json");
const fileContent = {
"extends": extended,
"default": true,
"MD001": false
};
const extendedContent = {
"MD001": true,
"MD041": true
};
const fsApi = {
"access": (p, m, cb) => {
t.is(p, extended);
return (cb || m)();
},
"readFile": (p, o, cb) => {
switch (p) {
case extended:
t.is(p, extended);
return cb(null, JSON.stringify(extendedContent));
default:
return t.fail();
}
}
};
extendConfig(fileContent, file, undefined, fsApi)
.then((actual) => {
t.truthy(fileContent.extends);
const expected = {
...extendedContent,
...fileContent
};
// @ts-ignore
delete expected.extends;
t.deepEqual(actual, expected, "Config object not correct.");
resolve();
});
}));