marked/test/index.js

557 lines
12 KiB
JavaScript
Raw Normal View History

2011-08-22 22:34:20 -05:00
#!/usr/bin/env node
/**
* marked tests
* Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed)
* https://github.com/chjj/marked
*/
/**
* Modules
*/
2011-08-22 22:34:20 -05:00
var fs = require('fs')
, path = require('path')
2018-01-06 01:11:16 -06:00
, fm = require('front-matter')
2013-01-20 17:44:25 -06:00
, marked = require('../');
2013-01-03 08:29:40 -06:00
/**
* Load Tests
*/
2013-01-03 08:29:40 -06:00
function load() {
2018-01-02 13:57:24 -06:00
var dir = __dirname + '/compiled_tests'
2013-01-06 21:54:44 -06:00
, files = {}
, list
, file
2018-01-06 01:11:16 -06:00
, content
2013-01-06 21:54:44 -06:00
, i
, l;
2011-08-22 22:34:20 -05:00
2013-01-06 21:54:44 -06:00
list = fs
2011-08-22 23:16:25 -05:00
.readdirSync(dir)
.filter(function(file) {
return path.extname(file) !== '.html';
})
.sort(function(a, b) {
a = path.basename(a).toLowerCase().charCodeAt(0);
b = path.basename(b).toLowerCase().charCodeAt(0);
return a > b ? 1 : (a < b ? -1 : 0);
});
2011-08-22 22:34:20 -05:00
2013-01-06 21:54:44 -06:00
i = 0;
l = list.length;
2011-08-22 23:16:25 -05:00
for (; i < l; i++) {
file = path.join(dir, list[i]);
2018-01-06 01:11:16 -06:00
content = fm(fs.readFileSync(file, 'utf8'));
2011-08-22 23:16:25 -05:00
files[path.basename(file)] = {
2018-01-06 01:11:16 -06:00
options: content.attributes,
text: content.body,
2011-08-22 23:16:25 -05:00
html: fs.readFileSync(file.replace(/[^.]+$/, 'html'), 'utf8')
};
}
return files;
2013-01-03 08:29:40 -06:00
}
2011-08-22 23:16:25 -05:00
/**
* Test Runner
*/
function runTests(engine, options) {
if (typeof engine !== 'function') {
options = engine;
engine = null;
}
var engine = engine || marked
, options = options || {}
2013-01-06 21:54:44 -06:00
, files = options.files || load()
2013-01-06 21:43:11 -06:00
, complete = 0
, failed = 0
, failures = []
2011-08-22 23:16:25 -05:00
, keys = Object.keys(files)
2013-01-06 21:43:11 -06:00
, i = 0
, len = keys.length
2011-08-22 23:16:25 -05:00
, filename
, file
2018-01-06 01:11:16 -06:00
, opts
2011-08-22 23:16:25 -05:00
, text
2013-01-06 21:43:11 -06:00
, html
, j
, l;
2011-08-22 22:34:20 -05:00
if (options.marked) {
marked.setOptions(options.marked);
}
2011-10-22 08:36:34 -05:00
main:
2013-01-06 21:43:11 -06:00
for (; i < len; i++) {
filename = keys[i];
2011-08-22 23:16:25 -05:00
file = files[filename];
2018-01-06 01:11:16 -06:00
opts = Object.keys(file.options);
2011-08-22 23:16:25 -05:00
if (marked._original) {
marked.defaults = marked._original;
delete marked._original;
}
2018-01-06 01:11:16 -06:00
if (opts.length) {
marked._original = marked.defaults;
marked.defaults = {};
Object.keys(marked._original).forEach(function(key) {
marked.defaults[key] = marked._original[key];
});
2018-01-06 01:11:16 -06:00
opts.forEach(function(key) {
if (marked.defaults.hasOwnProperty(key)) {
2018-01-06 01:11:16 -06:00
marked.defaults[key] = file.options[key];
}
});
}
2013-08-07 06:40:08 -05:00
2011-10-22 08:36:34 -05:00
try {
text = engine(file.text).replace(/\s/g, '');
html = file.html.replace(/\s/g, '');
2018-01-02 13:53:05 -06:00
} catch (e) {
2011-10-22 08:36:34 -05:00
console.log('%s failed.', filename);
throw e;
2011-08-22 22:34:20 -05:00
}
2013-01-06 21:43:11 -06:00
j = 0;
l = html.length;
2011-08-22 23:16:25 -05:00
2013-01-06 21:43:11 -06:00
for (; j < l; j++) {
if (text[j] !== html[j]) {
2013-02-12 01:17:28 -06:00
failed++;
failures.push(filename);
2013-02-12 01:17:28 -06:00
2011-08-22 22:34:20 -05:00
text = text.substring(
2013-01-06 21:43:11 -06:00
Math.max(j - 30, 0),
Math.min(j + 30, text.length));
2011-08-22 22:34:20 -05:00
html = html.substring(
2013-01-06 21:43:11 -06:00
Math.max(j - 30, 0),
Math.min(j + 30, html.length));
2011-08-22 22:34:20 -05:00
console.log(
2011-10-22 08:36:34 -05:00
'\n#%d. %s failed at offset %d. Near: "%s".\n',
2013-01-06 21:43:11 -06:00
i + 1, filename, j, text);
console.log('\nGot:\n%s\n', text.trim() || text);
console.log('\nExpected:\n%s\n', html.trim() || html);
2013-01-06 21:43:11 -06:00
if (options.stop) {
2011-08-22 22:34:20 -05:00
break main;
}
2013-01-06 21:43:11 -06:00
continue main;
2011-08-22 22:34:20 -05:00
}
}
2013-01-06 21:43:11 -06:00
complete++;
console.log('#%d. %s completed.', i + 1, filename);
2011-08-22 22:34:20 -05:00
}
2013-01-06 21:43:11 -06:00
console.log('%d/%d tests completed successfully.', complete, len);
if (failed) console.log('%d/%d tests failed.', failed, len);
return !failed;
2013-01-03 08:29:40 -06:00
}
2011-08-22 22:34:20 -05:00
/**
* Benchmark a function
*/
2013-01-03 08:29:40 -06:00
function bench(name, func) {
2013-01-06 21:54:44 -06:00
var files = bench.files || load();
if (!bench.files) {
bench.files = files;
2013-01-06 21:43:11 -06:00
// Change certain tests to allow
// comparison to older benchmark times.
2012-01-09 08:18:24 -06:00
fs.readdirSync(__dirname + '/new').forEach(function(name) {
2013-01-06 21:54:44 -06:00
if (path.extname(name) === '.html') return;
2018-01-02 11:59:52 -06:00
if (name === 'main.md') return;
2012-01-09 08:18:24 -06:00
delete files[name];
});
2013-01-06 21:54:44 -06:00
2018-01-02 11:59:52 -06:00
files['backslash_escapes.md'] = {
text: 'hello world \\[how](are you) today'
};
2013-01-06 21:54:44 -06:00
2018-01-02 11:59:52 -06:00
files['main.md'].text = files['main.md'].text.replace('* * *\n\n', '');
}
2011-08-22 23:16:25 -05:00
var start = Date.now()
, times = 1000
, keys = Object.keys(files)
2013-01-06 21:43:11 -06:00
, i
2011-08-22 23:16:25 -05:00
, l = keys.length
, filename
, file;
while (times--) {
for (i = 0; i < l; i++) {
filename = keys[i];
file = files[filename];
func(file.text);
}
}
console.log('%s completed in %dms.', name, Date.now() - start);
2013-01-03 08:29:40 -06:00
}
2011-08-22 23:16:25 -05:00
/**
* Benchmark all engines
*/
function runBench(options) {
var options = options || {};
// Non-GFM, Non-pedantic
2013-01-09 13:43:36 -06:00
marked.setOptions({
gfm: false,
tables: false,
breaks: false,
pedantic: false,
sanitize: false,
smartLists: false
2013-01-09 13:43:36 -06:00
});
if (options.marked) {
marked.setOptions(options.marked);
}
2013-01-03 08:29:40 -06:00
bench('marked', marked);
2011-08-22 23:16:25 -05:00
// GFM
2013-01-09 13:43:36 -06:00
marked.setOptions({
gfm: true,
tables: false,
breaks: false,
pedantic: false,
sanitize: false,
smartLists: false
2013-01-09 13:43:36 -06:00
});
if (options.marked) {
marked.setOptions(options.marked);
}
2013-01-03 08:29:40 -06:00
bench('marked (gfm)', marked);
// Pedantic
2013-01-09 13:43:36 -06:00
marked.setOptions({
gfm: false,
tables: false,
breaks: false,
pedantic: true,
sanitize: false,
smartLists: false
2013-01-09 13:43:36 -06:00
});
if (options.marked) {
marked.setOptions(options.marked);
}
2013-01-03 08:29:40 -06:00
bench('marked (pedantic)', marked);
2012-02-19 20:29:35 -06:00
2013-05-28 20:32:11 -05:00
// showdown
try {
bench('showdown (reuse converter)', (function() {
var Showdown = require('showdown');
2018-01-06 01:16:46 -06:00
var convert = new Showdown.Converter();
2013-05-28 20:32:11 -05:00
return function(text) {
return convert.makeHtml(text);
};
})());
bench('showdown (new converter)', (function() {
var Showdown = require('showdown');
return function(text) {
2018-01-06 01:16:46 -06:00
var convert = new Showdown.Converter();
2013-05-28 20:32:11 -05:00
return convert.makeHtml(text);
};
})());
} catch (e) {
2018-01-06 14:59:21 -06:00
console.log('Could not bench showdown. (Error: %s)', e.message);
2013-05-28 20:32:11 -05:00
}
2011-08-22 23:16:25 -05:00
2018-01-06 01:44:10 -06:00
// markdown-it
try {
bench('markdown-it', (function() {
var MarkdownIt = require('markdown-it');
var md = new MarkdownIt();
return function(text) {
return md.render(text);
};
})());
} catch (e) {
2018-01-06 14:59:21 -06:00
console.log('Could not bench markdown-it. (Error: %s)', e.message);
2018-01-06 01:44:10 -06:00
}
2013-05-28 20:32:11 -05:00
// markdown.js
try {
2018-01-06 01:44:10 -06:00
bench('markdown.js', (function() {
var markdown = require('markdown').markdown;
return function(text) {
return markdown.toHTML(text);
};
})());
2013-05-28 20:32:11 -05:00
} catch (e) {
2018-01-06 14:59:21 -06:00
console.log('Could not bench markdown.js. (Error: %s)', e.message);
2013-05-28 20:32:11 -05:00
}
2018-01-06 01:44:10 -06:00
return true;
2013-01-03 08:29:40 -06:00
}
2011-08-22 22:34:20 -05:00
/**
* A simple one-time benchmark
*/
function time(options) {
var options = options || {};
if (options.marked) {
marked.setOptions(options.marked);
}
2013-01-03 08:29:40 -06:00
bench('marked', marked);
}
2011-10-22 08:36:34 -05:00
2013-02-12 01:17:28 -06:00
/**
* Markdown Test Suite Fixer
* This function is responsible for "fixing"
* the markdown test suite. There are
* certain aspects of the suite that
* are strange or might make tests
* fail for reasons unrelated to
* conformance.
*/
2018-01-02 13:53:05 -06:00
function fix() {
2018-01-02 13:57:24 -06:00
['compiled_tests', 'original', 'new'].forEach(function(dir) {
2013-02-12 01:17:28 -06:00
try {
2018-01-06 01:44:32 -06:00
fs.mkdirSync(path.resolve(__dirname, dir), 0o755);
2013-02-12 01:17:28 -06:00
} catch (e) {
;
}
});
// rm -rf tests
2018-01-02 13:57:24 -06:00
fs.readdirSync(path.resolve(__dirname, 'compiled_tests')).forEach(function(file) {
fs.unlinkSync(path.resolve(__dirname, 'compiled_tests', file));
2013-02-12 01:17:28 -06:00
});
// cp -r original tests
fs.readdirSync(path.resolve(__dirname, 'original')).forEach(function(file) {
2018-01-06 01:11:16 -06:00
var text = fs.readFileSync(path.resolve(__dirname, 'original', file));
2018-01-06 10:46:21 -06:00
if (file === 'hard_wrapped_paragraphs_with_list_like_lines.md') {
2018-01-06 01:19:43 -06:00
text = '---\ngfm: false\n---\n' + text;
}
2018-01-06 10:46:21 -06:00
2018-01-06 01:11:16 -06:00
fs.writeFileSync(path.resolve(__dirname, 'compiled_tests', file), text);
2013-02-12 01:17:28 -06:00
});
// node fix.js
2018-01-02 13:57:24 -06:00
var dir = __dirname + '/compiled_tests';
2013-02-12 01:17:28 -06:00
fs.readdirSync(dir).filter(function(file) {
return path.extname(file) === '.html';
}).forEach(function(file) {
var file = path.join(dir, file)
, html = fs.readFileSync(file, 'utf8');
// fix unencoded quotes
2013-02-12 01:17:28 -06:00
html = html
.replace(/='([^\n']*)'(?=[^<>\n]*>)/g, '=&__APOS__;$1&__APOS__;')
.replace(/="([^\n"]*)"(?=[^<>\n]*>)/g, '=&__QUOT__;$1&__QUOT__;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;')
.replace(/&__QUOT__;/g, '"')
.replace(/&__APOS__;/g, '\'');
// add heading id's
html = html.replace(/<(h[1-6])>([^<]+)<\/\1>/g, function(s, h, text) {
var id = text
.replace(/&#39;/g, '\'')
.replace(/&quot;/g, '"')
.replace(/&gt;/g, '>')
.replace(/&lt;/g, '<')
.replace(/&amp;/g, '&');
id = id.toLowerCase().replace(/[^\w]+/g, '-');
return '<' + h + ' id="' + id + '">' + text + '</' + h + '>';
});
2013-02-12 01:17:28 -06:00
fs.writeFileSync(file, html);
});
// turn <hr /> into <hr>
fs.readdirSync(dir).forEach(function(file) {
var file = path.join(dir, file)
, text = fs.readFileSync(file, 'utf8');
text = text.replace(/(<|&lt;)hr\s*\/(>|&gt;)/g, '$1hr$2');
fs.writeFileSync(file, text);
});
// markdown does some strange things.
// it does not encode naked `>`, marked does.
(function() {
var file = dir + '/amps_and_angles_encoding.html';
var html = fs.readFileSync(file, 'utf8')
.replace('6 > 5.', '6 &gt; 5.');
fs.writeFileSync(file, html);
})();
// cp new/* tests/
fs.readdirSync(path.resolve(__dirname, 'new')).forEach(function(file) {
2018-01-02 13:57:24 -06:00
fs.writeFileSync(path.resolve(__dirname, 'compiled_tests', file),
2013-02-12 01:17:28 -06:00
fs.readFileSync(path.resolve(__dirname, 'new', file)));
});
}
/**
* Argument Parsing
*/
function parseArg(argv) {
var argv = process.argv.slice(2)
, options = {}
2018-01-06 11:56:47 -06:00
, opt = ""
, orphans = []
, arg;
function getarg() {
var arg = argv.shift();
if (arg.indexOf('--') === 0) {
// e.g. --opt
arg = arg.split('=');
if (arg.length > 1) {
// e.g. --opt=val
argv.unshift(arg.slice(1).join('='));
}
arg = arg[0];
} else if (arg[0] === '-') {
if (arg.length > 2) {
// e.g. -abc
argv = arg.substring(1).split('').map(function(ch) {
return '-' + ch;
}).concat(argv);
arg = argv.shift();
} else {
// e.g. -a
}
} else {
// e.g. foo
}
return arg;
2013-01-03 08:29:40 -06:00
}
while (argv.length) {
arg = getarg();
switch (arg) {
2013-02-12 01:17:28 -06:00
case '-f':
case '--fix':
case 'fix':
2018-01-02 13:53:05 -06:00
if (options.fix !== false) {
options.fix = true;
}
break;
case '--no-fix':
case 'no-fix':
options.fix = false;
2013-02-12 01:17:28 -06:00
break;
case '-b':
case '--bench':
options.bench = true;
break;
case '-s':
case '--stop':
options.stop = true;
break;
case '-t':
case '--time':
options.time = true;
break;
default:
if (arg.indexOf('--') === 0) {
2018-01-06 11:56:47 -06:00
opt = camelize(arg.replace(/^--(no-)?/, ''));
if (!marked.defaults.hasOwnProperty(opt)) {
continue;
}
options.marked = options.marked || {};
if (arg.indexOf('--no-') === 0) {
options.marked[opt] = typeof marked.defaults[opt] !== 'boolean'
? null
: false;
} else {
options.marked[opt] = typeof marked.defaults[opt] !== 'boolean'
? argv.shift()
: true;
}
} else {
orphans.push(arg);
}
break;
}
2011-08-22 23:16:25 -05:00
}
2013-01-03 08:29:40 -06:00
return options;
}
/**
* Helpers
*/
function camelize(text) {
return text.replace(/(\w)-(\w)/g, function(_, a, b) {
return a + b.toUpperCase();
2013-01-06 21:43:11 -06:00
});
2013-01-03 08:29:40 -06:00
}
/**
* Main
*/
function main(argv) {
var opt = parseArg();
2018-01-02 13:53:05 -06:00
if (opt.fix !== false) {
fix();
}
2013-02-12 01:17:28 -06:00
if (opt.fix) {
2018-01-06 11:56:47 -06:00
// only run fix
2018-01-02 13:53:05 -06:00
return;
2013-02-12 01:17:28 -06:00
}
if (opt.bench) {
return runBench(opt);
}
if (opt.time) {
return time(opt);
}
2013-08-04 07:12:52 -05:00
return runTests(opt);
}
/**
* Execute
*/
2013-01-03 08:29:40 -06:00
if (!module.parent) {
process.title = 'marked';
2013-08-04 07:12:52 -05:00
process.exit(main(process.argv.slice()) ? 0 : 1);
2011-08-22 23:16:25 -05:00
} else {
exports = main;
exports.main = main;
exports.runTests = runTests;
exports.runBench = runBench;
exports.load = load;
exports.bench = bench;
module.exports = exports;
2011-08-22 23:16:25 -05:00
}