textlint-tester
Version:
testing tool for textlint rule.
345 lines (341 loc) • 13.5 kB
JavaScript
// LICENSE : MIT
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.TextLintTester = exports.createTestLinter = exports.createTextlintKernelDescriptor = void 0;
const assert = __importStar(require("assert"));
const test_util_1 = require("./test-util");
const kernel_1 = require("@textlint/kernel");
const feature_flag_1 = require("@textlint/feature-flag");
const textlint_plugin_text_1 = __importDefault(require("@textlint/textlint-plugin-text"));
const textlint_plugin_markdown_1 = __importDefault(require("@textlint/textlint-plugin-markdown"));
const promises_1 = __importDefault(require("fs/promises"));
const path_1 = __importDefault(require("path"));
const hasOwnProperty = Object.prototype.hasOwnProperty;
const globalObject = globalThis;
// Type guard helper
function isObjectWithProperty(obj, property) {
return typeof obj === "object" && obj !== null && property in obj;
}
const describe = typeof globalObject.describe === "function"
? globalObject.describe
: function (_text, method) {
return method.apply(this);
};
const it = typeof globalObject.it === "function"
? globalObject.it
: function (_text, method) {
return method.apply(this);
};
/**
* get fixer function from ruleCreator
* if not found, throw error
* @param {((...args: any[]) => any)|Object} ruleCreator
* @param {string} ruleName
*/
function assertHasFixer(ruleCreator, ruleName) {
if (isObjectWithProperty(ruleCreator, "fixer") && typeof ruleCreator.fixer === "function") {
return;
}
if (typeof ruleCreator === "function") {
return;
}
throw new Error(`Not found \`fixer\` function in the ruleCreator: ${ruleName}`);
}
function assertTestConfig(testConfig) {
assert.notEqual(testConfig, null, "TestConfig is null");
assert.notEqual(Object.keys(testConfig).length === 0 && testConfig.constructor === Object, true, "TestConfig is empty");
assert.ok(Array.isArray(testConfig.rules), "TestConfig.rules should be an array");
assert.ok(testConfig.rules.length > 0, "TestConfig.rules should have at least one rule");
testConfig.rules.forEach((rule) => {
assert.ok(hasOwnProperty.call(rule, "ruleId"), "ruleId property not found");
assert.ok(hasOwnProperty.call(rule, "rule"), "rule property not found");
});
if (typeof testConfig.plugins !== "undefined") {
assert.ok(Array.isArray(testConfig.plugins), "TestConfig.plugins should be an array");
testConfig.plugins.forEach((plugin) => {
assert.ok(hasOwnProperty.call(plugin, "pluginId"), "pluginId property not found");
assert.ok(hasOwnProperty.call(plugin, "plugin"), "plugin property not found");
});
}
}
function isTestConfig(arg) {
if (hasOwnProperty.call(arg, "rules")) {
return true;
}
if ((isObjectWithProperty(arg, "fixer") && typeof arg.fixer === "function") || typeof arg === "function") {
return false;
}
return true;
}
function createTestPluginSet(testConfigPlugins) {
const testPluginSet = {
plugins: {},
pluginOptions: {}
};
testConfigPlugins.forEach((plugin) => {
var _a;
const pluginName = plugin.pluginId;
const pluginOptions = (_a = plugin.options) !== null && _a !== void 0 ? _a : true;
testPluginSet.plugins[pluginName] = plugin.plugin;
testPluginSet.pluginOptions[pluginName] = pluginOptions;
});
return testPluginSet;
}
const builtInPlugins = [
{
pluginId: "@textlint/textlint-plugin-text",
plugin: textlint_plugin_text_1.default,
options: true
},
{
pluginId: "@textlint/textlint-plugin-markdown",
plugin: textlint_plugin_markdown_1.default,
options: true
}
];
const createTextlintKernelDescriptor = ({ testName, testRuleDefinition, testCaseOptions }) => {
if (isTestConfig(testRuleDefinition)) {
const testConfig = testRuleDefinition;
assertTestConfig(testConfig);
// Note: testCaseOptions is not supported and it will be just ignored.
// Assertion check it
// > Could not specify options property in valid object when TestConfig was passed. Use TestConfig.rules.options.
const testPluginSet = createTestPluginSet(testConfig.plugins || []);
const plugins = [
...builtInPlugins,
...Object.keys(testPluginSet.plugins).map((pluginId) => {
return {
pluginId,
plugin: testPluginSet.plugins[pluginId],
options: testPluginSet.pluginOptions[pluginId]
};
})
];
return new kernel_1.TextlintKernelDescriptor({
rules: testConfig.rules,
filterRules: [],
plugins
});
}
else {
return new kernel_1.TextlintKernelDescriptor({
rules: [
{
ruleId: testName,
rule: testRuleDefinition,
options: testCaseOptions
}
],
filterRules: [],
plugins: builtInPlugins
});
}
};
exports.createTextlintKernelDescriptor = createTextlintKernelDescriptor;
const createTestLinter = (textlintKernelDescriptor) => {
const kernel = new kernel_1.TextlintKernel();
return {
async lintText(text, ext) {
return kernel.lintText(text, {
ext,
...textlintKernelDescriptor.toKernelOptions()
});
},
async lintFile(filePath) {
const text = await promises_1.default.readFile(filePath, "utf-8");
const ext = path_1.default.extname(filePath);
return kernel.lintText(text, {
ext,
filePath,
...textlintKernelDescriptor.toKernelOptions()
});
},
async fixText(text, ext) {
return kernel.fixText(text, {
ext,
...textlintKernelDescriptor.toKernelOptions()
});
},
async fixFile(filePath) {
const text = await promises_1.default.readFile(filePath, "utf-8");
const ext = path_1.default.extname(filePath);
return kernel.fixText(text, {
ext,
filePath,
...textlintKernelDescriptor.toKernelOptions()
});
}
};
};
exports.createTestLinter = createTestLinter;
class TextLintTester {
constructor() {
if (typeof feature_flag_1.coreFlags === "object") {
feature_flag_1.coreFlags.runningTester = true;
}
}
testValidPattern(testName, param, valid) {
const text = typeof valid === "object" ? valid.text : valid;
const inputPath = typeof valid === "object" ? valid.inputPath : undefined;
const ext = typeof valid === "object" && valid.ext !== undefined ? valid.ext : ".md";
const options = typeof valid === "object" && valid.options !== undefined ? valid.options : undefined;
const description = typeof valid === "object" && valid.description !== undefined ? valid.description : undefined;
const textlint = (0, exports.createTestLinter)((0, exports.createTextlintKernelDescriptor)({
testName,
testRuleDefinition: param,
testCaseOptions: options
}));
const textCaseName = `${inputPath || text}`;
it(textCaseName, () => {
if (inputPath) {
return (0, test_util_1.testValid)({
textlint,
inputPath,
description
});
}
else if (text !== undefined && ext) {
return (0, test_util_1.testValid)({
textlint,
text,
ext,
description
});
}
throw new Error(`valid should have text or inputPath property.
valid: [ "text", { text: "text" }, { inputPath: "path/to/file" } ]
`);
});
}
testInvalidPattern(testName, param, invalid) {
const errors = invalid.errors;
const inputPath = invalid.inputPath;
const text = invalid.text;
const ext = invalid.ext !== undefined ? invalid.ext : ".md";
const options = invalid.options;
const description = invalid.description;
const textlint = (0, exports.createTestLinter)((0, exports.createTextlintKernelDescriptor)({
testName,
testRuleDefinition: param,
testCaseOptions: options
}));
const testCaseName = `${inputPath || text}`;
it(testCaseName, () => {
if (inputPath) {
return (0, test_util_1.testInvalid)({
textlint,
inputPath,
errors,
description
});
}
else if (text !== undefined && ext) {
return (0, test_util_1.testInvalid)({
textlint,
text,
ext,
errors,
description
});
}
throw new Error(`invalid should have { text } or { inputPath } property.
invalid: [ { text: "text", errors: [...] }, { inputPath: "path/to/file", errors: [...] } ]
`);
});
// --fix
if (hasOwnProperty.call(invalid, "output")) {
it(`Fixer: ${testCaseName}`, () => {
if (isTestConfig(param)) {
param.rules.forEach((rule) => {
assertHasFixer(rule.rule, rule.ruleId);
});
}
else {
assertHasFixer(param, testName);
}
let promise;
if (inputPath !== undefined) {
promise = textlint.fixFile(inputPath);
}
else if (text !== undefined) {
promise = textlint.fixText(text, ext);
}
else {
throw new Error("Should set `text` or `inputPath`");
}
return promise.then((result) => {
const output = invalid.output;
assert.strictEqual(result.output, output);
});
});
}
}
/**
* run test for textlint rule.
* @param {string} name name is name of the test or rule
* @param {TextlintRuleModule|TestConfig} testRuleDefinition param is TextlintRuleCreator or TestConfig
* @param {string[]|object[]} [valid]
* @param {object[]} [invalid]
*/
run(name, testRuleDefinition, { valid = [], invalid = [] }) {
if (isTestConfig(testRuleDefinition)) {
assertTestConfig(testRuleDefinition);
if (valid) {
valid.forEach((validCase) => {
assert.ok(!hasOwnProperty.call(validCase, "options"), "Could not specify options property in valid object when TestConfig was passed. Use TestConfig.rules.options.");
});
}
if (invalid) {
invalid.forEach((invalidCase) => {
assert.ok(!hasOwnProperty.call(invalidCase, "options"), "Could not specify options property in invalid object when TestConfig was passed. Use TestConfig.rules.options.");
});
}
}
describe(name, () => {
invalid.forEach((state) => {
this.testInvalidPattern(name, testRuleDefinition, state);
});
valid.forEach((state) => {
this.testValidPattern(name, testRuleDefinition, state);
});
});
}
}
exports.TextLintTester = TextLintTester;
//# sourceMappingURL=textlint-tester.js.map