markdown-doctest
Version:
Test all the code in your markdown docs!
158 lines (157 loc) • 6.43 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.printResults = exports.runTests = void 0;
var fs_1 = require("fs");
var vm_1 = require("vm");
var core_1 = require("@babel/core");
var preset_env_1 = require("@babel/preset-env");
var chalk_1 = require("chalk");
function flatten(arr) {
return Array.prototype.concat.apply([], arr);
}
var parse_code_snippets_from_markdown_1 = require("./parse-code-snippets-from-markdown");
function runTests(files, config) {
var results = files
.map(read)
.map(parse_code_snippets_from_markdown_1.default)
.map(testFile(config));
return flatten(results);
}
exports.runTests = runTests;
function read(fileName) {
return { contents: fs_1.readFileSync(fileName, "utf8"), fileName: fileName };
}
function makeTestSandbox(config) {
function sandboxRequire(moduleName) {
for (var regexRequire in config.regexRequire) {
var regex = new RegExp(regexRequire);
var match = regex.exec(moduleName);
var handler = config.regexRequire[regexRequire];
if (match) {
return handler.apply(void 0, match);
}
}
if (config.require[moduleName] === undefined) {
throw moduleNotFoundError(moduleName);
}
return config.require[moduleName];
}
var sandboxConsole = {
log: function () { return null; },
};
var sandboxGlobals = { require: sandboxRequire, console: sandboxConsole };
var sandbox = Object.assign({}, sandboxGlobals, config.globals);
return sandbox;
}
function testFile(config) {
return function testFileWithConfig(args) {
var codeSnippets = args.codeSnippets;
var fileName = args.fileName;
var shareCodeInFile = args.shareCodeInFile;
var results;
if (shareCodeInFile) {
var sandbox = makeTestSandbox(config);
results = codeSnippets.map(test(config, fileName, sandbox));
}
else {
results = codeSnippets.map(test(config, fileName));
}
return results;
};
}
function test(config, filename, sandbox) {
return function (codeSnippet) {
if (codeSnippet.skip) {
return { status: "skip", codeSnippet: codeSnippet, stack: "" };
}
var success = false;
var stack = "";
var code = codeSnippet.code;
if (config.transformCode) {
try {
code = config.transformCode(code);
}
catch (e) {
return { status: "fail", codeSnippet: codeSnippet, stack: "Encountered an error while transforming snippet: \n" + e.stack };
}
}
var perSnippetSandbox;
if (sandbox === undefined) {
perSnippetSandbox = makeTestSandbox(config);
}
if (config.beforeEach) {
config.beforeEach();
}
var options = {
presets: [preset_env_1.default],
};
try {
if (config.babel !== false) {
code = core_1.transformSync(code, options).code;
}
vm_1.runInNewContext(code, perSnippetSandbox || sandbox);
success = true;
}
catch (e) {
stack = e.stack || "";
}
var status = success ? "pass" : "fail";
process.stdout.write(success ? chalk_1.default.green(".") : chalk_1.default.red("x"));
return { status: status, codeSnippet: codeSnippet, stack: stack };
};
}
function printResults(results) {
results.filter(function (result) { return result.status === "fail"; }).forEach(printFailure);
var passingCount = results.filter(function (result) { return result.status === "pass"; })
.length;
var failingCount = results.filter(function (result) { return result.status === "fail"; })
.length;
var skippingCount = results.filter(function (result) { return result.status === "skip"; })
.length;
function successfulRun() {
return failingCount === 0;
}
console.log(chalk_1.default.green("Passed: " + passingCount));
if (skippingCount > 0) {
console.log(chalk_1.default.yellow("Skipped: " + skippingCount));
}
if (successfulRun()) {
console.log(chalk_1.default.green("\nSuccess!"));
}
else {
console.log(chalk_1.default.red("Failed: " + failingCount));
}
}
exports.printResults = printResults;
function printFailure(result) {
console.log(chalk_1.default.red("Failed - " + markDownErrorLocation(result)));
var stackDetails = relevantStackDetails(result.stack);
console.log(stackDetails);
var variableNotDefined = stackDetails.match(/(\w+) is not defined/);
if (variableNotDefined) {
var variableName = variableNotDefined[1];
console.log("You can declare " + chalk_1.default.blue(variableName) + " in the " + chalk_1.default.blue("globals") + " section in " + chalk_1.default.grey(".markdown-doctest-setup.js"));
console.log("\nFor example:\n" + chalk_1.default.grey("// .markdown-doctest-setup.js") + "\nmodule.exports = {\n globals: {\n " + chalk_1.default.blue(variableName) + ": ...\n }\n}\n ");
}
}
function relevantStackDetails(stack) {
var match = stack.match(/([\w\W]*?)at eval/) ||
stack.match(/([\w\W]*)at [\w*\/]*?doctest.js/);
if (match !== null) {
return match[1];
}
return stack;
}
function moduleNotFoundError(moduleName) {
return new Error("\nAttempted to require '" + chalk_1.default.blue(moduleName) + "' but was not found in config.\nYou need to include it in the require section of your " + chalk_1.default.grey(".markdown-doctest-setup.js") + " file.\n\nFor example:\n" + chalk_1.default.grey("// .markdown-doctest-setup.js") + "\nmodule.exports = {\n require: {\n " + chalk_1.default.blue("'" + moduleName + "': require('" + moduleName + "')") + "\n }\n}\n ");
}
function markDownErrorLocation(result) {
var match = result.stack.match(/eval.*<.*>:(\d+):(\d+)/);
if (match) {
var mdLineNumber = parseInt(match[1], 10);
var columnNumber = parseInt(match[2], 10);
var lineNumber = result.codeSnippet.lineNumber + mdLineNumber;
return result.codeSnippet.fileName + ":" + lineNumber + ":" + columnNumber;
}
return result.codeSnippet.fileName + ":" + result.codeSnippet.lineNumber;
}
;