From 99d1fd00e07411c844c950200101eb3c16fcc6e9 Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Thu, 25 Apr 2019 07:47:23 -0500 Subject: [PATCH 01/22] add bench.json --- package.json | 2 +- test/bench.js | 261 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 test/bench.js diff --git a/package.json b/package.json index faeb155b62..a8fa199205 100644 --- a/package.json +++ b/package.json @@ -58,7 +58,7 @@ "test:lint": "eslint bin/marked .", "test:redos": "eslint --plugin vuln-regex-detector --rule '\"vuln-regex-detector/no-vuln-regex\": 2' lib/marked.js", "test:node4": "npx node@4 ./node_modules/jasmine/bin/jasmine.js --config=jasmine.json", - "bench": "node test --bench", + "bench": "node test/bench.js", "lint": "eslint --fix bin/marked .", "build": "uglifyjs lib/marked.js -cm --comments /Copyright/ -o marked.min.js", "preversion": "npm run build && (git diff --quiet || git commit -am 'minify')" diff --git a/test/bench.js b/test/bench.js new file mode 100644 index 0000000000..ebada7073f --- /dev/null +++ b/test/bench.js @@ -0,0 +1,261 @@ +#!/usr/bin/env node + +const path = require('path'); +const fs = require('fs'); +const htmlDiffer = require('./helpers/html-differ.js'); + +let marked = require('../'); + +function load() { + let folder = path.resolve(__dirname, './specs/commonmark'); + const files = fs.readdirSync(folder); + return files.reduce((arr, file) => { + if (file.match(/\.json$/)) { + return arr.concat(require(`${folder}/${file}`)); + } + return arr; + }, []); +} + +function runBench(options) { + options = options || {}; + const specs = load(); + + // Non-GFM, Non-pedantic + marked.setOptions({ + gfm: false, + tables: false, + breaks: false, + pedantic: false, + sanitize: false, + smartLists: false + }); + if (options.marked) { + marked.setOptions(options.marked); + } + bench('marked', specs, marked); + + // GFM + marked.setOptions({ + gfm: true, + tables: false, + breaks: false, + pedantic: false, + sanitize: false, + smartLists: false + }); + if (options.marked) { + marked.setOptions(options.marked); + } + bench('marked (gfm)', specs, marked); + + // Pedantic + marked.setOptions({ + gfm: false, + tables: false, + breaks: false, + pedantic: true, + sanitize: false, + smartLists: false + }); + if (options.marked) { + marked.setOptions(options.marked); + } + bench('marked (pedantic)', specs, marked); + + try { + bench('commonmark', specs, (() => { + const commonmark = require('commonmark'); + const parser = new commonmark.Parser(); + const writer = new commonmark.HtmlRenderer(); + return function (text) { + return writer.render(parser.parse(text)); + }; + })()); + } catch (e) { + console.error('Could not bench commonmark. (Error: %s)', e.message); + } + + try { + bench('markdown-it', specs, (() => { + const MarkdownIt = require('markdown-it'); + const md = new MarkdownIt(); + return md.render.bind(md); + })()); + } catch (e) { + console.error('Could not bench markdown-it. (Error: %s)', e.message); + } + + try { + bench('markdown.js', specs, (() => { + const md = require('markdown').markdown; + return md.toHTML.bind(md); + })()); + } catch (e) { + console.error('Could not bench markdown.js. (Error: %s)', e.message); + } +} + +function bench(name, specs, engine) { + const before = process.hrtime(); + for (let i = 0; i < 1e3; i++) { + for (const spec of specs) { + engine(spec.markdown); + } + } + const elapsed = process.hrtime(before); + const ms = prettyElapsedTime(elapsed).toFixed(); + + const results = []; + for (const spec of specs) { + results.push({ + expected: spec.html, + actual: engine(spec.markdown) + }); + } + const correct = results.reduce((num, result) => num + (htmlDiffer.isEqual(result.expected, result.actual) ? 1 : 0), 0); + const percent = (correct / results.length * 100).toFixed(2); + + console.log('%s completed in %sms and passed %s%', name, ms, percent); +} + +/** + * A simple one-time benchmark + */ +function time(options) { + options = options || {}; + const specs = load(); + if (options.marked) { + marked.setOptions(options.marked); + } + bench('marked', specs, marked); +} + +/** + * Argument Parsing + */ +function parseArg(argv) { + argv = argv.slice(2); + + const options = {}; + const orphans = []; + + function getarg() { + let 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(ch => { + return `-${ch}`; + }).concat(argv); + arg = argv.shift(); + } else { + // e.g. -a + } + } else { + // e.g. foo + } + + return arg; + } + + const defaults = marked.getDefaults(); + + while (argv.length) { + let arg = getarg(); + switch (arg) { + case '-t': + case '--time': + options.time = true; + break; + case '-m': + case '--minified': + options.minified = true; + break; + default: + if (arg.indexOf('--') === 0) { + const opt = camelize(arg.replace(/^--(no-)?/, '')); + if (!defaults.hasOwnProperty(opt)) { + continue; + } + options.marked = options.marked || {}; + if (arg.indexOf('--no-') === 0) { + options.marked[opt] = typeof defaults[opt] !== 'boolean' + ? null + : false; + } else { + options.marked[opt] = typeof defaults[opt] !== 'boolean' + ? argv.shift() + : true; + } + } else { + orphans.push(arg); + } + break; + } + } + + if (orphans.length > 0) { + console.error(); + console.error('The following arguments are not used:'); + orphans.forEach(arg => console.error(` ${arg}`)); + console.error(); + } + + return options; +} + +/** + * Helpers + */ +function camelize(text) { + return text.replace(/(\w)-(\w)/g, (_, a, b) => a + b.toUpperCase()); +} + +/** + * Main + */ +function main(argv) { + const opt = parseArg(argv); + + if (opt.minified) { + marked = require('../marked.min.js'); + } + + if (opt.time) { + time(opt); + } else { + runBench(opt); + } +} + +/** + * returns time to millisecond granularity + */ +function prettyElapsedTime(hrtimeElapsed) { + const seconds = hrtimeElapsed[0]; + const frac = Math.round(hrtimeElapsed[1] / 1e3) / 1e3; + return seconds * 1e3 + frac; +} + +if (!module.parent) { + process.title = 'marked bench'; + main(process.argv.slice()); +} else { + exports = main; + exports.main = main; + exports.time = time; + exports.runBench = runBench; + exports.load = load; + exports.bench = bench; + module.exports = exports; +} From b1174c21d803cb1083b9555695d1f316ec09ef4c Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Thu, 25 Apr 2019 07:52:11 -0500 Subject: [PATCH 02/22] update run-spec for node 4 --- test/specs/run-spec.js | 72 +++++++++++++++++++++++++++++++++++------- 1 file changed, 61 insertions(+), 11 deletions(-) diff --git a/test/specs/run-spec.js b/test/specs/run-spec.js index 3af0aa4565..94080aae00 100644 --- a/test/specs/run-spec.js +++ b/test/specs/run-spec.js @@ -1,4 +1,60 @@ +'use strict'; + +function node4Polyfills() { + // https://github.com/uxitten/polyfill/blob/master/string.polyfill.js + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd + if (!String.prototype.padEnd) { + // eslint-disable-next-line no-extend-native + String.prototype.padEnd = function padEnd(targetLength, padString) { + targetLength = targetLength >> 0; // floor if number or convert non-number to 0; + padString = String((typeof padString !== 'undefined' ? padString : ' ')); + if (this.length > targetLength) { + return String(this); + } else { + targetLength = targetLength - this.length; + if (targetLength > padString.length) { + padString += padString.repeat(targetLength / padString.length); // append to original to ensure we are longer than needed + } + return String(this) + padString.slice(0, targetLength); + } + }; + } + + // https://github.com/uxitten/polyfill/blob/master/string.polyfill.js + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart + if (!String.prototype.padStart) { + // eslint-disable-next-line no-extend-native + String.prototype.padStart = function padStart(targetLength, padString) { + targetLength = targetLength >> 0; // truncate if number, or convert non-number to 0; + padString = String(typeof padString !== 'undefined' ? padString : ' '); + if (this.length >= targetLength) { + return String(this); + } else { + targetLength = targetLength - this.length; + if (targetLength > padString.length) { + padString += padString.repeat(targetLength / padString.length); // append to original to ensure we are longer than needed + } + return padString.slice(0, targetLength) + String(this); + } + }; + } +} + +function outputCompletionTable(title, specs, longestName, maxSpecs) { + const maxSpecsLen = ('' + maxSpecs).length; + const spaces = maxSpecsLen * 2 + longestName + 11; + console.log('-'.padEnd(spaces + 4, '-')); + console.log(`| ${title.padStart(Math.ceil((spaces + title.length) / 2)).padEnd(spaces)} |`); + console.log(`| ${' '.padEnd(spaces)} |`); + for (const section in specs) { + console.log(`| ${section.padEnd(longestName)} ${('' + specs[section].pass).padStart(maxSpecsLen)} of ${('' + specs[section].total).padStart(maxSpecsLen)} ${(100 * specs[section].pass / specs[section].total).toFixed().padStart(4)}% |`); + } + console.log('-'.padEnd(spaces + 4, '-')); + console.log(); +} + function runSpecs(title, file, options) { + options = options || {}; const json = require(file); let longestName = 0; let maxSpecs = 0; @@ -20,19 +76,13 @@ function runSpecs(title, file, options) { return obj; }, {}); + outputCompletionTable(title, specs, longestName, maxSpecs); + describe(title, () => { - const maxSpecsLen = ('' + maxSpecs).length; - const spaces = maxSpecsLen * 2 + longestName + 11; - console.log('-'.padEnd(spaces + 4, '-')); - console.log(`| ${title.padStart(Math.ceil((spaces + title.length) / 2)).padEnd(spaces)} |`); - console.log(`| ${' '.padEnd(spaces)} |`); Object.keys(specs).forEach(section => { - console.log(`| ${section.padEnd(longestName)} ${('' + specs[section].pass).padStart(maxSpecsLen)} of ${('' + specs[section].total).padStart(maxSpecsLen)} ${(100 * specs[section].pass / specs[section].total).toFixed().padStart(4)}% |`); describe(section, () => { specs[section].specs.forEach((spec) => { - if (options) { - spec.options = Object.assign({}, options, (spec.options || {})); - } + spec.options = Object.assign({}, options, (spec.options || {})); (spec.only ? fit : it)('should ' + (spec.shouldFail ? 'fail' : 'pass') + ' example ' + spec.example, () => { if (spec.shouldFail) { expect(spec).not.toRender(spec.html); @@ -43,10 +93,10 @@ function runSpecs(title, file, options) { }); }); }); - console.log('-'.padEnd(spaces + 4, '-')); - console.log(); }); }; +node4Polyfills(); + runSpecs('GFM 0.29', './gfm/gfm.0.29.json', {gfm: true}); runSpecs('CommonMark 0.29', './commonmark/commonmark.0.29.json', {headerIds: false}); From 7deade0635fd9da8924f10c72c74f424358ac9b0 Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Thu, 25 Apr 2019 09:41:25 -0500 Subject: [PATCH 03/22] move update-specs.js --- test/specs/commonmark/getSpecs.js | 24 ---------- test/specs/gfm/getSpecs.js | 44 ----------------- test/update-specs.js | 80 +++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 68 deletions(-) delete mode 100644 test/specs/commonmark/getSpecs.js delete mode 100644 test/specs/gfm/getSpecs.js create mode 100644 test/update-specs.js diff --git a/test/specs/commonmark/getSpecs.js b/test/specs/commonmark/getSpecs.js deleted file mode 100644 index f22e00c0c5..0000000000 --- a/test/specs/commonmark/getSpecs.js +++ /dev/null @@ -1,24 +0,0 @@ -const fetch = require('node-fetch'); -const marked = require('../../../'); -const htmlDiffer = require('../../helpers/html-differ.js'); -const fs = require('fs'); - -fetch('https://raw.githubusercontent.com/commonmark/commonmark.js/master/package.json') - .then(res => res.json()) - .then(pkg => pkg.version.replace(/^(\d+\.\d+).*$/, '$1')) - .then(version => - fetch(`https://spec.commonmark.org/${version}/spec.json`) - .then(res => res.json()) - .then(specs => { - specs.forEach(spec => { - const html = marked(spec.markdown, {headerIds: false}); - if (!htmlDiffer.isEqual(html, spec.html)) { - spec.shouldFail = true; - } - }); - fs.writeFileSync(`commonmark.${version}.json`, JSON.stringify(specs, null, 2) + '\n'); - }) - ) - .catch((err) => { - console.error(err); - }); diff --git a/test/specs/gfm/getSpecs.js b/test/specs/gfm/getSpecs.js deleted file mode 100644 index 2746bdbbcb..0000000000 --- a/test/specs/gfm/getSpecs.js +++ /dev/null @@ -1,44 +0,0 @@ -const fetch = require('node-fetch'); -const cheerio = require('cheerio'); -const marked = require('../../../'); -const htmlDiffer = require('../../helpers/html-differ.js'); -const fs = require('fs'); - -fetch('https://github.github.com/gfm/') - .then(res => res.text()) - .then(html => cheerio.load(html)) - .then($ => { - const version = $('.version').text().match(/\d+\.\d+/)[0]; - if (!version) { - throw new Error('No version found'); - } - const specs = []; - $('.extension').each((i, ext) => { - const section = $('.definition', ext).text().trim().replace(/^\d+\.\d+(.*?) \(extension\)[\s\S]*$/, '$1'); - $('.example', ext).each((j, exa) => { - const example = +$(exa).attr('id').replace(/\D/g, ''); - const markdown = $('.language-markdown', exa).text().trim(); - const html = $('.language-html', exa).text().trim(); - specs.push({ - section, - html, - markdown, - example - }); - }); - }); - - return [version, specs]; - }) - .then(([version, specs]) => { - specs.forEach(spec => { - const html = marked(spec.markdown, {gfm: true}); - if (!htmlDiffer.isEqual(html, spec.html)) { - spec.shouldFail = true; - } - }); - fs.writeFileSync(`gfm.${version}.json`, JSON.stringify(specs, null, 2) + '\n'); - }) - .catch((err) => { - console.error(err); - }); diff --git a/test/update-specs.js b/test/update-specs.js new file mode 100644 index 0000000000..7190a3757c --- /dev/null +++ b/test/update-specs.js @@ -0,0 +1,80 @@ +const fetch = require('node-fetch'); +const cheerio = require('cheerio'); +const marked = require('../../../'); +const htmlDiffer = require('../../helpers/html-differ.js'); +const fs = require('fs'); +const path = require('path'); + +function removeFiles(dir) { + fs.readdirSync(dir).forEach(file => { + fs.unlinkSync(path.join(dir, file)); + }); +} + +function updateCommonmark(dir) { + return fetch('https://raw.githubusercontent.com/commonmark/commonmark.js/master/package.json') + .then(res => res.json()) + .then(pkg => pkg.version.replace(/^(\d+\.\d+).*$/, '$1')) + .then(version => + fetch(`https://spec.commonmark.org/${version}/spec.json`) + .then(res => res.json()) + .then(specs => { + specs.forEach(spec => { + const html = marked(spec.markdown, {headerIds: false}); + if (!htmlDiffer.isEqual(html, spec.html)) { + spec.shouldFail = true; + } + }); + removeFiles(dir); + fs.writeFileSync(path.resolve(dir, `./commonmark.${version}.json`), JSON.stringify(specs, null, 2) + '\n'); + }) + ) + .catch((err) => { + console.error(err); + }); +} + +function updateGfm(dir) { + return fetch('https://github.github.com/gfm/') + .then(res => res.text()) + .then(html => cheerio.load(html)) + .then($ => { + const version = $('.version').text().match(/\d+\.\d+/)[0]; + if (!version) { + throw new Error('No version found'); + } + const specs = []; + $('.extension').each((i, ext) => { + const section = $('.definition', ext).text().trim().replace(/^\d+\.\d+(.*?) \(extension\)[\s\S]*$/, '$1'); + $('.example', ext).each((j, exa) => { + const example = +$(exa).attr('id').replace(/\D/g, ''); + const markdown = $('.language-markdown', exa).text().trim(); + const html = $('.language-html', exa).text().trim(); + specs.push({ + section, + html, + markdown, + example + }); + }); + }); + + return [version, specs]; + }) + .then(([version, specs]) => { + specs.forEach(spec => { + const html = marked(spec.markdown, {gfm: true}); + if (!htmlDiffer.isEqual(html, spec.html)) { + spec.shouldFail = true; + } + }); + removeFiles(dir); + fs.writeFileSync(path.resolve(dir, `./gfm.${version}.json`), JSON.stringify(specs, null, 2) + '\n'); + }) + .catch((err) => { + console.error(err); + }); +} + +updateCommonmark(path.resolve(__dirname, './specs/commonmark')); +updateGfm(path.resolve(__dirname, './specs/gfm')); From 10ce168d863a4c58e3f090f28960da72a564d35c Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Thu, 25 Apr 2019 09:42:02 -0500 Subject: [PATCH 04/22] remove files --- test/README | 10 - test/index.js | 551 ------------------------------------------ test/json-to-files.js | 62 ----- 3 files changed, 623 deletions(-) delete mode 100644 test/README delete mode 100644 test/index.js delete mode 100644 test/json-to-files.js diff --git a/test/README b/test/README deleted file mode 100644 index 51f6560b9e..0000000000 --- a/test/README +++ /dev/null @@ -1,10 +0,0 @@ -In this directory: - -# -# MarkdownTester -- Run tests for Markdown implementations -# -# Copyright (c) 2004-2005 John Gruber -# -# - -Partially modified for testing purposes. diff --git a/test/index.js b/test/index.js deleted file mode 100644 index 5027ee5b90..0000000000 --- a/test/index.js +++ /dev/null @@ -1,551 +0,0 @@ -#!/usr/bin/env node -'use strict'; -// 'use strict' is here so we can use let and const in node 4 - -/** - * marked tests - * Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed) - * https://github.com/markedjs/marked - */ - -/** - * Modules - */ - -const fs = require('fs'); -const path = require('path'); -const fm = require('front-matter'); -const g2r = require('glob-to-regexp'); -let marked = require('../'); -const htmlDiffer = require('./helpers/html-differ.js'); - -/** - * Load Tests - */ - -function load(options) { - options = options || {}; - const dir = path.join(__dirname, 'compiled_tests'); - const glob = g2r(options.glob || '*', { extended: true }); - - const list = fs - .readdirSync(dir) - .filter(file => { - return path.extname(file) === '.md'; - }) - .sort(); - - const files = list.reduce((obj, item) => { - const name = path.basename(item, '.md'); - if (glob.test(name)) { - const file = path.join(dir, item); - const content = fm(fs.readFileSync(file, 'utf8')); - - obj[name] = { - options: content.attributes, - text: content.body, - html: fs.readFileSync(file.replace(/[^.]+$/, 'html'), 'utf8') - }; - } - return obj; - }, {}); - - if (options.bench || options.time) { - if (!options.glob) { - // Change certain tests to allow - // comparison to older benchmark times. - fs.readdirSync(path.join(__dirname, 'new')).forEach(name => { - if (path.extname(name) === '.html') return; - if (name === 'main.md') return; - delete files[name]; - }); - } - - if (files['backslash_escapes.md']) { - files['backslash_escapes.md'] = { - text: 'hello world \\[how](are you) today' - }; - } - - if (files['main.md']) { - files['main.md'].text = files['main.md'].text.replace('* * *\n\n', ''); - } - } - - return files; -} - -/** - * Test Runner - */ - -function runTests(engine, options) { - if (typeof engine !== 'function') { - options = engine; - engine = null; - } - - engine = engine || marked; - options = options || {}; - - let succeeded = 0; - let failed = 0; - const files = options.files || load(options); - const filenames = Object.keys(files); - - if (options.marked) { - marked.setOptions(options.marked); - } - - for (let i = 0; i < filenames.length; i++) { - const filename = filenames[i]; - const file = files[filename]; - - const success = testFile(engine, file, filename, i + 1); - - if (success) { - succeeded++; - } else { - failed++; - if (options.stop) { - break; - } - } - } - - console.log('\n%d/%d tests completed successfully.', succeeded, filenames.length); - if (failed) console.log('%d/%d tests failed.', failed, filenames.length); - - return !failed; -} - -/** - * Test a file - */ - -function testFile(engine, file, filename, index) { - const opts = Object.keys(file.options); - - if (marked._original) { - marked.defaults = marked._original; - delete marked._original; - } - - console.log('#%d. Test %s', index, filename); - - if (opts.length) { - marked._original = marked.defaults; - marked.defaults = {}; - Object.keys(marked._original).forEach(key => { - marked.defaults[key] = marked._original[key]; - }); - opts.forEach(key => { - if (marked.defaults.hasOwnProperty(key)) { - marked.defaults[key] = file.options[key]; - } - }); - } - - const before = process.hrtime(); - - let text, html, elapsed; - try { - text = engine(file.text); - html = file.html; - } catch (e) { - elapsed = process.hrtime(before); - console.log('\n failed in %dms\n', prettyElapsedTime(elapsed)); - throw e; - } - - elapsed = process.hrtime(before); - - if (htmlDiffer.isEqual(text, html)) { - if (elapsed[0] > 0) { - console.log('\n failed because it took too long.\n\n passed in %dms\n', prettyElapsedTime(elapsed)); - return false; - } - console.log(' passed in %dms', prettyElapsedTime(elapsed)); - return true; - } - - const diff = htmlDiffer.firstDiff(text, html); - - console.log('\n failed in %dms', prettyElapsedTime(elapsed)); - console.log(' Expected: %s', diff.expected); - console.log(' Actual: %s\n', diff.actual); - return false; -} - -/** - * Benchmark a function - */ - -function bench(name, files, engine) { - const start = Date.now(); - - for (let i = 0; i < 1000; i++) { - for (const filename in files) { - engine(files[filename].text); - } - } - - const end = Date.now(); - - console.log('%s completed in %dms.', name, end - start); -} - -/** - * Benchmark all engines - */ - -function runBench(options) { - options = options || {}; - const files = load(options); - - // Non-GFM, Non-pedantic - marked.setOptions({ - gfm: false, - tables: false, - breaks: false, - pedantic: false, - sanitize: false, - smartLists: false - }); - if (options.marked) { - marked.setOptions(options.marked); - } - bench('marked', files, marked); - - // GFM - marked.setOptions({ - gfm: true, - tables: false, - breaks: false, - pedantic: false, - sanitize: false, - smartLists: false - }); - if (options.marked) { - marked.setOptions(options.marked); - } - bench('marked (gfm)', files, marked); - - // Pedantic - marked.setOptions({ - gfm: false, - tables: false, - breaks: false, - pedantic: true, - sanitize: false, - smartLists: false - }); - if (options.marked) { - marked.setOptions(options.marked); - } - bench('marked (pedantic)', files, marked); - - try { - bench('commonmark', files, (() => { - const commonmark = require('commonmark'); - const parser = new commonmark.Parser(); - const writer = new commonmark.HtmlRenderer(); - return function (text) { - return writer.render(parser.parse(text)); - }; - })()); - } catch (e) { - console.log('Could not bench commonmark. (Error: %s)', e.message); - } - - try { - bench('markdown-it', files, (() => { - const MarkdownIt = require('markdown-it'); - const md = new MarkdownIt(); - return md.render.bind(md); - })()); - } catch (e) { - console.log('Could not bench markdown-it. (Error: %s)', e.message); - } - - try { - bench('markdown.js', files, (() => { - const markdown = require('markdown').markdown; - return markdown.toHTML.bind(markdown); - })()); - } catch (e) { - console.log('Could not bench markdown.js. (Error: %s)', e.message); - } - - return true; -} - -/** - * A simple one-time benchmark - */ - -function time(options) { - options = options || {}; - const files = load(options); - if (options.marked) { - marked.setOptions(options.marked); - } - bench('marked', files, marked); - - return true; -} - -/** - * 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. - */ - -function fix() { - ['compiled_tests', 'original', 'new', 'redos'].forEach(dir => { - try { - fs.mkdirSync(path.resolve(__dirname, dir)); - } catch (e) { - // directory already exists - } - }); - - // rm -rf tests - fs.readdirSync(path.resolve(__dirname, 'compiled_tests')).forEach(file => { - fs.unlinkSync(path.resolve(__dirname, 'compiled_tests', file)); - }); - - // cp -r original tests - fs.readdirSync(path.resolve(__dirname, 'original')).forEach(file => { - let text = fs.readFileSync(path.resolve(__dirname, 'original', file), 'utf8'); - - if (path.extname(file) === '.md') { - if (fm.test(text)) { - text = fm(text); - text = `---\n${text.frontmatter}\ngfm: false\n---\n${text.body}`; - } else { - text = `---\ngfm: false\n---\n${text}`; - } - } - - fs.writeFileSync(path.resolve(__dirname, 'compiled_tests', file), text); - }); - - // node fix.js - const dir = path.join(__dirname, 'compiled_tests'); - - fs.readdirSync(dir).filter(file => { - return path.extname(file) === '.html'; - }).forEach(file => { - file = path.join(dir, file); - let html = fs.readFileSync(file, 'utf8'); - - // fix unencoded quotes - html = html - .replace(/='([^\n']*)'(?=[^<>\n]*>)/g, '=&__APOS__;$1&__APOS__;') - .replace(/="([^\n"]*)"(?=[^<>\n]*>)/g, '=&__QUOT__;$1&__QUOT__;') - .replace(/"/g, '"') - .replace(/'/g, ''') - .replace(/&__QUOT__;/g, '"') - .replace(/&__APOS__;/g, '\''); - - fs.writeFileSync(file, html); - }); - - // turn
into
- fs.readdirSync(dir).forEach(file => { - file = path.join(dir, file); - let text = fs.readFileSync(file, 'utf8'); - - text = text.replace(/(<|<)hr\s*\/(>|>)/g, '$1hr$2'); - - fs.writeFileSync(file, text); - }); - - // markdown does some strange things. - // it does not encode naked `>`, marked does. - { - const file = `${dir}/amps_and_angles_encoding.html`; - const html = fs.readFileSync(file, 'utf8') - .replace('6 > 5.', '6 > 5.'); - - fs.writeFileSync(file, html); - } - - // cp new/* tests/ - fs.readdirSync(path.resolve(__dirname, 'new')).forEach(file => { - fs.writeFileSync(path.resolve(__dirname, 'compiled_tests', file), - fs.readFileSync(path.resolve(__dirname, 'new', file))); - }); - - // cp redos/* tests/ - fs.readdirSync(path.resolve(__dirname, 'redos')).forEach(file => { - fs.writeFileSync(path.resolve(__dirname, 'compiled_tests', file), - fs.readFileSync(path.resolve(__dirname, 'redos', file))); - }); -} - -/** - * Argument Parsing - */ - -function parseArg(argv) { - argv = argv.slice(2); - - const options = {}; - const orphans = []; - - function getarg() { - let 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(ch => { - return `-${ch}`; - }).concat(argv); - arg = argv.shift(); - } else { - // e.g. -a - } - } else { - // e.g. foo - } - - return arg; - } - - while (argv.length) { - let arg = getarg(); - switch (arg) { - case '-f': - case '--fix': - case 'fix': - if (options.fix !== false) { - options.fix = true; - } - break; - case '--no-fix': - case 'no-fix': - options.fix = false; - 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; - case '-m': - case '--minified': - options.minified = true; - break; - case '--glob': - arg = argv.shift(); - options.glob = arg.replace(/^=/, ''); - break; - default: - if (arg.indexOf('--') === 0) { - const 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; - } - } - - return options; -} - -/** - * Helpers - */ - -function camelize(text) { - return text.replace(/(\w)-(\w)/g, (_, a, b) => a + b.toUpperCase()); -} - -/** - * Main - */ - -function main(argv) { - const opt = parseArg(argv); - - if (opt.fix !== false) { - fix(); - } - - if (opt.fix) { - // only run fix - return; - } - - if (opt.bench) { - return runBench(opt); - } - - if (opt.time) { - return time(opt); - } - - if (opt.minified) { - marked = require('../marked.min.js'); - } - return runTests(opt); -} - -/** - * Execute - */ - -if (!module.parent) { - process.title = 'marked'; - process.exit(main(process.argv.slice()) ? 0 : 1); -} else { - exports = main; - exports.main = main; - exports.runTests = runTests; - exports.testFile = testFile; - exports.runBench = runBench; - exports.load = load; - exports.bench = bench; - module.exports = exports; -} - -// returns time to millisecond granularity -function prettyElapsedTime(hrtimeElapsed) { - const seconds = hrtimeElapsed[0]; - const frac = Math.round(hrtimeElapsed[1] / 1e3) / 1e3; - return seconds * 1e3 + frac; -} diff --git a/test/json-to-files.js b/test/json-to-files.js deleted file mode 100644 index d7e72aafe0..0000000000 --- a/test/json-to-files.js +++ /dev/null @@ -1,62 +0,0 @@ -const path = require('path'); -const fs = require('fs'); - -const folder = process.argv[2]; -const jsonFile = process.argv[3]; - -if (!folder || !jsonFile) { - console.log('node ./json-to-files.js {path to folder} {path to json file}'); - process.exit(1); -} - -const specs = require(jsonFile); - -const files = specs.reduce((obj, spec) => { - if (!obj[spec.section]) { - obj[spec.section] = { - md: [], - html: [], - options: {} - }; - } - - obj[spec.section].md.push(spec.markdown); - obj[spec.section].html.push(spec.html); - Object.assign(obj[spec.section].options, spec.options); - - return obj; -}, {}); - -try { - fs.mkdirSync(folder, {recursive: true}); -} catch (ex) { - // already exists -} - -for (const section in files) { - const file = files[section]; - const name = section.toLowerCase().replace(' ', '_'); - const frontMatter = Object.keys(file.options).map(opt => { - let value = file.options[opt]; - if (typeof value !== 'string') { - value = JSON.stringify(value); - } - return `${opt}: ${value}`; - }).join('\n'); - - let markdown = file.md.join('\n\n'); - if (frontMatter) { - markdown = `---\n${frontMatter}\n---\n\n${markdown}`; - } - const html = file.html.join('\n\n'); - - const mdFile = path.resolve(folder, `${name}.md`); - const htmlFile = path.resolve(folder, `${name}.html`); - - if (fs.existsSync(mdFile) || fs.existsSync(htmlFile)) { - throw new Error(`${name} already exists.`); - } - - fs.writeFileSync(mdFile, markdown); - fs.writeFileSync(htmlFile, html); -} From 975ec3415213e84c06857523ce3bf2d5a45e2b59 Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Thu, 25 Apr 2019 09:42:38 -0500 Subject: [PATCH 05/22] load all specs --- test/helpers/load.js | 127 +++++ test/new/cm_autolinks.html | 91 ---- test/new/cm_autolinks.md | 96 ---- test/new/cm_blockquotes.html | 233 -------- test/new/cm_blockquotes.md | 189 ------- test/new/cm_html_blocks.html | 300 ---------- test/new/cm_html_blocks.md | 312 ----------- test/new/cm_link_defs.html | 115 ---- test/new/cm_link_defs.md | 157 ------ test/new/cm_links.html | 397 -------------- test/new/cm_links.md | 515 ------------------ test/new/cm_raw_html.html | 77 --- test/new/cm_raw_html.md | 78 --- test/new/cm_strong_and_em.html | 7 - test/new/cm_strong_and_em.md | 7 - test/new/cm_thematic_breaks.html | 106 ---- test/new/cm_thematic_breaks.md | 98 ---- test/new/gfm_autolinks.html | 83 --- test/new/gfm_autolinks.md | 83 --- test/new/gfm_break.html | 1 - test/new/gfm_break.md | 6 - test/new/gfm_code.html | 21 - test/new/gfm_code.md | 43 -- test/new/gfm_code_hr_list.html | 52 -- test/new/gfm_code_hr_list.md | 53 -- test/new/gfm_em.html | 1 - test/new/gfm_em.md | 1 - test/new/gfm_hashtag.html | 5 - test/new/gfm_hashtag.md | 8 - test/new/gfm_links_invalid.html | 1 - test/new/gfm_links_invalid.md | 4 - test/new/gfm_tables.html | 37 -- test/new/gfm_tables.md | 21 - test/{ => specs}/new/adjacent_lists.html | 0 test/{ => specs}/new/adjacent_lists.md | 0 test/{ => specs}/new/autolink_lines.html | 0 test/{ => specs}/new/autolink_lines.md | 0 test/{ => specs}/new/autolinks.html | 0 test/{ => specs}/new/autolinks.md | 0 .../{ => specs}/new/blockquote_list_item.html | 0 test/{ => specs}/new/blockquote_list_item.md | 0 .../new/case_insensitive_refs.html | 0 test/{ => specs}/new/case_insensitive_refs.md | 0 test/{ => specs}/new/code_spans.html | 0 test/{ => specs}/new/code_spans.md | 0 test/{ => specs}/new/def_blocks.html | 0 test/{ => specs}/new/def_blocks.md | 0 test/{ => specs}/new/double_link.html | 0 test/{ => specs}/new/double_link.md | 0 test/{ => specs}/new/em_2char.html | 0 test/{ => specs}/new/em_2char.md | 0 .../{ => specs}/new/emphasis_extra tests.html | 0 test/{ => specs}/new/emphasis_extra tests.md | 0 test/{ => specs}/new/escaped_angles.html | 0 test/{ => specs}/new/escaped_angles.md | 0 test/{ => specs}/new/headings_id.html | 0 test/{ => specs}/new/headings_id.md | 0 test/{ => specs}/new/hr_list_break.html | 0 test/{ => specs}/new/hr_list_break.md | 0 test/{ => specs}/new/html_comments.html | 0 test/{ => specs}/new/html_comments.md | 0 test/{ => specs}/new/html_no_new_line.html | 0 test/{ => specs}/new/html_no_new_line.md | 0 test/{ => specs}/new/images.html | 0 test/{ => specs}/new/images.md | 0 test/{ => specs}/new/lazy_blockquotes.html | 0 test/{ => specs}/new/lazy_blockquotes.md | 0 test/{ => specs}/new/link_lt.html | 0 test/{ => specs}/new/link_lt.md | 0 test/{ => specs}/new/link_tick_redos.html | 0 test/{ => specs}/new/link_tick_redos.md | 0 test/{ => specs}/new/links.html | 0 test/{ => specs}/new/links.md | 0 test/{ => specs}/new/list_item_text.html | 0 test/{ => specs}/new/list_item_text.md | 0 test/{ => specs}/new/list_table.html | 0 test/{ => specs}/new/list_table.md | 0 test/{ => specs}/new/main.html | 0 test/{ => specs}/new/main.md | 0 test/{ => specs}/new/mangle_xss.html | 0 test/{ => specs}/new/mangle_xss.md | 0 test/{ => specs}/new/nested_code.html | 0 test/{ => specs}/new/nested_code.md | 0 test/{ => specs}/new/nested_em.html | 0 test/{ => specs}/new/nested_em.md | 0 test/{ => specs}/new/nested_square_link.html | 0 test/{ => specs}/new/nested_square_link.md | 0 test/{ => specs}/new/nogfm_hashtag.html | 0 test/{ => specs}/new/nogfm_hashtag.md | 0 test/{ => specs}/new/not_a_link.html | 0 test/{ => specs}/new/not_a_link.md | 0 test/{ => specs}/new/ref_paren.html | 0 test/{ => specs}/new/ref_paren.md | 0 test/{ => specs}/new/relative_urls.html | 0 test/{ => specs}/new/relative_urls.md | 0 test/{ => specs}/new/same_bullet.html | 0 test/{ => specs}/new/same_bullet.md | 0 test/{ => specs}/new/sanitize_links.html | 0 test/{ => specs}/new/sanitize_links.md | 0 test/{ => specs}/new/smartypants.html | 0 test/{ => specs}/new/smartypants.md | 0 test/{ => specs}/new/smartypants_code.html | 0 test/{ => specs}/new/smartypants_code.md | 0 test/{ => specs}/new/table_cells.html | 0 test/{ => specs}/new/table_cells.md | 0 test/{ => specs}/new/toplevel_paragraphs.html | 0 test/{ => specs}/new/toplevel_paragraphs.md | 0 test/{ => specs}/new/tricky_list.html | 0 test/{ => specs}/new/tricky_list.md | 0 test/{ => specs}/new/uppercase_hex.html | 0 test/{ => specs}/new/uppercase_hex.md | 0 .../original/amps_and_angles_encoding.html | 0 .../original/amps_and_angles_encoding.md | 0 test/{ => specs}/original/auto_links.html | 0 test/{ => specs}/original/auto_links.md | 0 .../original/backslash_escapes.html | 0 .../{ => specs}/original/backslash_escapes.md | 0 .../blockquotes_with_code_blocks.html | 0 .../original/blockquotes_with_code_blocks.md | 0 test/{ => specs}/original/code_blocks.html | 0 test/{ => specs}/original/code_blocks.md | 0 test/{ => specs}/original/code_spans.html | 0 test/{ => specs}/original/code_spans.md | 0 ...apped_paragraphs_with_list_like_lines.html | 0 ...wrapped_paragraphs_with_list_like_lines.md | 0 .../original/horizontal_rules.html | 0 test/{ => specs}/original/horizontal_rules.md | 0 .../original/inline_html_advanced.html | 0 .../original/inline_html_advanced.md | 0 .../original/inline_html_comments.html | 0 .../original/inline_html_comments.md | 0 .../original/inline_html_simple.html | 0 .../original/inline_html_simple.md | 0 .../original/links_inline_style.html | 0 .../original/links_inline_style.md | 0 .../original/links_reference_style.html | 0 .../original/links_reference_style.md | 0 .../original/links_shortcut_references.html | 0 .../original/links_shortcut_references.md | 0 .../original/literal_quotes_in_titles.html | 0 .../original/literal_quotes_in_titles.md | 0 .../markdown_documentation_basics.html | 0 .../original/markdown_documentation_basics.md | 0 .../markdown_documentation_syntax.html | 0 .../original/markdown_documentation_syntax.md | 0 .../original/nested_blockquotes.html | 0 .../original/nested_blockquotes.md | 0 .../original/ordered_and_unordered_lists.html | 0 .../original/ordered_and_unordered_lists.md | 0 test/specs/original/specs-spec.js | 12 - .../original/strong_and_em_together.html | 0 .../original/strong_and_em_together.md | 0 test/{ => specs}/original/tabs.html | 0 test/{ => specs}/original/tabs.md | 0 test/{ => specs}/original/tidyness.html | 0 test/{ => specs}/original/tidyness.md | 0 test/specs/redos-spec.js | 24 - test/{ => specs}/redos/link_redos.html | 0 test/{ => specs}/redos/link_redos.md | 0 test/{ => specs}/redos/quadratic_br.js | 0 test/{ => specs}/redos/quadratic_email.js | 0 .../{ => specs}/redos/redos_html_closing.html | 0 test/{ => specs}/redos/redos_html_closing.md | 0 test/{ => specs}/redos/redos_nolink.html | 0 test/{ => specs}/redos/redos_nolink.md | 0 test/specs/run-spec.js | 100 +--- 166 files changed, 146 insertions(+), 3315 deletions(-) create mode 100644 test/helpers/load.js delete mode 100644 test/new/cm_autolinks.html delete mode 100644 test/new/cm_autolinks.md delete mode 100644 test/new/cm_blockquotes.html delete mode 100644 test/new/cm_blockquotes.md delete mode 100644 test/new/cm_html_blocks.html delete mode 100644 test/new/cm_html_blocks.md delete mode 100644 test/new/cm_link_defs.html delete mode 100644 test/new/cm_link_defs.md delete mode 100644 test/new/cm_links.html delete mode 100644 test/new/cm_links.md delete mode 100644 test/new/cm_raw_html.html delete mode 100644 test/new/cm_raw_html.md delete mode 100644 test/new/cm_strong_and_em.html delete mode 100644 test/new/cm_strong_and_em.md delete mode 100644 test/new/cm_thematic_breaks.html delete mode 100644 test/new/cm_thematic_breaks.md delete mode 100644 test/new/gfm_autolinks.html delete mode 100644 test/new/gfm_autolinks.md delete mode 100644 test/new/gfm_break.html delete mode 100644 test/new/gfm_break.md delete mode 100644 test/new/gfm_code.html delete mode 100644 test/new/gfm_code.md delete mode 100644 test/new/gfm_code_hr_list.html delete mode 100644 test/new/gfm_code_hr_list.md delete mode 100644 test/new/gfm_em.html delete mode 100644 test/new/gfm_em.md delete mode 100644 test/new/gfm_hashtag.html delete mode 100644 test/new/gfm_hashtag.md delete mode 100644 test/new/gfm_links_invalid.html delete mode 100644 test/new/gfm_links_invalid.md delete mode 100644 test/new/gfm_tables.html delete mode 100644 test/new/gfm_tables.md rename test/{ => specs}/new/adjacent_lists.html (100%) rename test/{ => specs}/new/adjacent_lists.md (100%) rename test/{ => specs}/new/autolink_lines.html (100%) rename test/{ => specs}/new/autolink_lines.md (100%) rename test/{ => specs}/new/autolinks.html (100%) rename test/{ => specs}/new/autolinks.md (100%) rename test/{ => specs}/new/blockquote_list_item.html (100%) rename test/{ => specs}/new/blockquote_list_item.md (100%) rename test/{ => specs}/new/case_insensitive_refs.html (100%) rename test/{ => specs}/new/case_insensitive_refs.md (100%) rename test/{ => specs}/new/code_spans.html (100%) rename test/{ => specs}/new/code_spans.md (100%) rename test/{ => specs}/new/def_blocks.html (100%) rename test/{ => specs}/new/def_blocks.md (100%) rename test/{ => specs}/new/double_link.html (100%) rename test/{ => specs}/new/double_link.md (100%) rename test/{ => specs}/new/em_2char.html (100%) rename test/{ => specs}/new/em_2char.md (100%) rename test/{ => specs}/new/emphasis_extra tests.html (100%) rename test/{ => specs}/new/emphasis_extra tests.md (100%) rename test/{ => specs}/new/escaped_angles.html (100%) rename test/{ => specs}/new/escaped_angles.md (100%) rename test/{ => specs}/new/headings_id.html (100%) rename test/{ => specs}/new/headings_id.md (100%) rename test/{ => specs}/new/hr_list_break.html (100%) rename test/{ => specs}/new/hr_list_break.md (100%) rename test/{ => specs}/new/html_comments.html (100%) rename test/{ => specs}/new/html_comments.md (100%) rename test/{ => specs}/new/html_no_new_line.html (100%) rename test/{ => specs}/new/html_no_new_line.md (100%) rename test/{ => specs}/new/images.html (100%) rename test/{ => specs}/new/images.md (100%) rename test/{ => specs}/new/lazy_blockquotes.html (100%) rename test/{ => specs}/new/lazy_blockquotes.md (100%) rename test/{ => specs}/new/link_lt.html (100%) rename test/{ => specs}/new/link_lt.md (100%) rename test/{ => specs}/new/link_tick_redos.html (100%) rename test/{ => specs}/new/link_tick_redos.md (100%) rename test/{ => specs}/new/links.html (100%) rename test/{ => specs}/new/links.md (100%) rename test/{ => specs}/new/list_item_text.html (100%) rename test/{ => specs}/new/list_item_text.md (100%) rename test/{ => specs}/new/list_table.html (100%) rename test/{ => specs}/new/list_table.md (100%) rename test/{ => specs}/new/main.html (100%) rename test/{ => specs}/new/main.md (100%) rename test/{ => specs}/new/mangle_xss.html (100%) rename test/{ => specs}/new/mangle_xss.md (100%) rename test/{ => specs}/new/nested_code.html (100%) rename test/{ => specs}/new/nested_code.md (100%) rename test/{ => specs}/new/nested_em.html (100%) rename test/{ => specs}/new/nested_em.md (100%) rename test/{ => specs}/new/nested_square_link.html (100%) rename test/{ => specs}/new/nested_square_link.md (100%) rename test/{ => specs}/new/nogfm_hashtag.html (100%) rename test/{ => specs}/new/nogfm_hashtag.md (100%) rename test/{ => specs}/new/not_a_link.html (100%) rename test/{ => specs}/new/not_a_link.md (100%) rename test/{ => specs}/new/ref_paren.html (100%) rename test/{ => specs}/new/ref_paren.md (100%) rename test/{ => specs}/new/relative_urls.html (100%) rename test/{ => specs}/new/relative_urls.md (100%) rename test/{ => specs}/new/same_bullet.html (100%) rename test/{ => specs}/new/same_bullet.md (100%) rename test/{ => specs}/new/sanitize_links.html (100%) rename test/{ => specs}/new/sanitize_links.md (100%) rename test/{ => specs}/new/smartypants.html (100%) rename test/{ => specs}/new/smartypants.md (100%) rename test/{ => specs}/new/smartypants_code.html (100%) rename test/{ => specs}/new/smartypants_code.md (100%) rename test/{ => specs}/new/table_cells.html (100%) rename test/{ => specs}/new/table_cells.md (100%) rename test/{ => specs}/new/toplevel_paragraphs.html (100%) rename test/{ => specs}/new/toplevel_paragraphs.md (100%) rename test/{ => specs}/new/tricky_list.html (100%) rename test/{ => specs}/new/tricky_list.md (100%) rename test/{ => specs}/new/uppercase_hex.html (100%) rename test/{ => specs}/new/uppercase_hex.md (100%) rename test/{ => specs}/original/amps_and_angles_encoding.html (100%) rename test/{ => specs}/original/amps_and_angles_encoding.md (100%) rename test/{ => specs}/original/auto_links.html (100%) rename test/{ => specs}/original/auto_links.md (100%) rename test/{ => specs}/original/backslash_escapes.html (100%) rename test/{ => specs}/original/backslash_escapes.md (100%) rename test/{ => specs}/original/blockquotes_with_code_blocks.html (100%) rename test/{ => specs}/original/blockquotes_with_code_blocks.md (100%) rename test/{ => specs}/original/code_blocks.html (100%) rename test/{ => specs}/original/code_blocks.md (100%) rename test/{ => specs}/original/code_spans.html (100%) rename test/{ => specs}/original/code_spans.md (100%) rename test/{ => specs}/original/hard_wrapped_paragraphs_with_list_like_lines.html (100%) rename test/{ => specs}/original/hard_wrapped_paragraphs_with_list_like_lines.md (100%) rename test/{ => specs}/original/horizontal_rules.html (100%) rename test/{ => specs}/original/horizontal_rules.md (100%) rename test/{ => specs}/original/inline_html_advanced.html (100%) rename test/{ => specs}/original/inline_html_advanced.md (100%) rename test/{ => specs}/original/inline_html_comments.html (100%) rename test/{ => specs}/original/inline_html_comments.md (100%) rename test/{ => specs}/original/inline_html_simple.html (100%) rename test/{ => specs}/original/inline_html_simple.md (100%) rename test/{ => specs}/original/links_inline_style.html (100%) rename test/{ => specs}/original/links_inline_style.md (100%) rename test/{ => specs}/original/links_reference_style.html (100%) rename test/{ => specs}/original/links_reference_style.md (100%) rename test/{ => specs}/original/links_shortcut_references.html (100%) rename test/{ => specs}/original/links_shortcut_references.md (100%) rename test/{ => specs}/original/literal_quotes_in_titles.html (100%) rename test/{ => specs}/original/literal_quotes_in_titles.md (100%) rename test/{ => specs}/original/markdown_documentation_basics.html (100%) rename test/{ => specs}/original/markdown_documentation_basics.md (100%) rename test/{ => specs}/original/markdown_documentation_syntax.html (100%) rename test/{ => specs}/original/markdown_documentation_syntax.md (100%) rename test/{ => specs}/original/nested_blockquotes.html (100%) rename test/{ => specs}/original/nested_blockquotes.md (100%) rename test/{ => specs}/original/ordered_and_unordered_lists.html (100%) rename test/{ => specs}/original/ordered_and_unordered_lists.md (100%) delete mode 100644 test/specs/original/specs-spec.js rename test/{ => specs}/original/strong_and_em_together.html (100%) rename test/{ => specs}/original/strong_and_em_together.md (100%) rename test/{ => specs}/original/tabs.html (100%) rename test/{ => specs}/original/tabs.md (100%) rename test/{ => specs}/original/tidyness.html (100%) rename test/{ => specs}/original/tidyness.md (100%) delete mode 100644 test/specs/redos-spec.js rename test/{ => specs}/redos/link_redos.html (100%) rename test/{ => specs}/redos/link_redos.md (100%) rename test/{ => specs}/redos/quadratic_br.js (100%) rename test/{ => specs}/redos/quadratic_email.js (100%) rename test/{ => specs}/redos/redos_html_closing.html (100%) rename test/{ => specs}/redos/redos_html_closing.md (100%) rename test/{ => specs}/redos/redos_nolink.html (100%) rename test/{ => specs}/redos/redos_nolink.md (100%) diff --git a/test/helpers/load.js b/test/helpers/load.js new file mode 100644 index 0000000000..6a7cd44f13 --- /dev/null +++ b/test/helpers/load.js @@ -0,0 +1,127 @@ +'use strict'; + +const fs = require('fs'); +const path = require('path'); +const fm = require('front-matter'); + +function node4Polyfills() { + // https://github.com/uxitten/polyfill/blob/master/string.polyfill.js + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd + if (!String.prototype.padEnd) { + // eslint-disable-next-line no-extend-native + String.prototype.padEnd = function padEnd(targetLength, padString) { + targetLength = targetLength >> 0; // floor if number or convert non-number to 0; + padString = String((typeof padString !== 'undefined' ? padString : ' ')); + if (this.length > targetLength) { + return String(this); + } else { + targetLength = targetLength - this.length; + if (targetLength > padString.length) { + padString += padString.repeat(targetLength / padString.length); // append to original to ensure we are longer than needed + } + return String(this) + padString.slice(0, targetLength); + } + }; + } + + // https://github.com/uxitten/polyfill/blob/master/string.polyfill.js + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart + if (!String.prototype.padStart) { + // eslint-disable-next-line no-extend-native + String.prototype.padStart = function padStart(targetLength, padString) { + targetLength = targetLength >> 0; // truncate if number, or convert non-number to 0; + padString = String(typeof padString !== 'undefined' ? padString : ' '); + if (this.length >= targetLength) { + return String(this); + } else { + targetLength = targetLength - this.length; + if (targetLength > padString.length) { + padString += padString.repeat(targetLength / padString.length); // append to original to ensure we are longer than needed + } + return padString.slice(0, targetLength) + String(this); + } + }; + } +} +node4Polyfills(); + +function outputCompletionTable(title, specs) { + let longestName = 0; + let maxSpecs = 0; + + for (const section in specs) { + longestName = Math.max(section.length, longestName); + maxSpecs = Math.max(specs[section].total, maxSpecs); + } + + const maxSpecsLen = ('' + maxSpecs).length; + const spaces = maxSpecsLen * 2 + longestName + 11; + + console.log('-'.padEnd(spaces + 4, '-')); + console.log(`| ${title.padStart(Math.ceil((spaces + title.length) / 2)).padEnd(spaces)} |`); + console.log(`| ${' '.padEnd(spaces)} |`); + for (const section in specs) { + console.log(`| ${section.padEnd(longestName)} ${('' + specs[section].pass).padStart(maxSpecsLen)} of ${('' + specs[section].total).padStart(maxSpecsLen)} ${(100 * specs[section].pass / specs[section].total).toFixed().padStart(4)}% |`); + } + console.log('-'.padEnd(spaces + 4, '-')); + console.log(); +} + +function loadFiles(dir) { + const files = fs.readdirSync(dir); + + return files.reduce((obj, file) => { + const ext = path.extname(file); + const name = path.basename(file, ext); + const absFile = path.join(dir, file); + let specs; + + switch (ext) { + case '.md': { + const content = fm(fs.readFileSync(absFile, 'utf8')); + specs = [{ + section: name, + example: 1, + markdown: content.body, + html: fs.readFileSync(absFile.replace(/[^.]+$/, 'html'), 'utf8'), + options: content.attributes + }]; + break; + } + case '.js': + case '.json': { + specs = require(absFile); + if (!Array.isArray(specs)) { + specs = [specs]; + } + break; + } + default: + return obj; + } + + for (const spec of specs) { + if (!spec.section) { + spec.section = name; + } + if (!obj[spec.section]) { + obj[spec.section] = { + total: 0, + pass: 0, + specs: [] + }; + } + + obj[spec.section].total++; + obj[spec.section].pass += spec.shouldFail ? 0 : 1; + obj[spec.section].specs.push(spec); + } + + return obj; + }, {}); +} + +module.exports = { + outputCompletionTable, + loadFiles +}; diff --git a/test/new/cm_autolinks.html b/test/new/cm_autolinks.html deleted file mode 100644 index e7ae0ee416..0000000000 --- a/test/new/cm_autolinks.html +++ /dev/null @@ -1,91 +0,0 @@ -

Here are some valid autolinks:

- -

Example 565

- -

http://foo.bar.baz

- -

Example 566

- -

http://foo.bar.baz/test?q=hello&id=22&boolean

- -

Example 567

- -

irc://foo.bar:2233/baz

- -

Example 568

- -

Uppercase is also fine:

- -

MAILTO:FOO@BAR.BAZ

- -

Note that many strings that count as absolute URIs for purposes of this spec are not valid URIs, because their schemes are not registered or because of other problems with their syntax:

- -

Example 569

- -

a+b+c:d

- -

Example 570

- -

made-up-scheme://foo,bar

- -

Example 571

- -

http://../

- -

Example 572

- -

localhost:5001/foo

- -

Example 573

- -

Spaces are not allowed in autolinks:

- -

<http://foo.bar/baz bim>

- -

Example 574

- -

Backslash-escapes do not work inside autolinks:

- -

http://example.com/\[\

- -

Examples of email autolinks:

- -

Example 575

- -

foo@bar.example.com

- -

Example 576

- -

foo+special@Bar.baz-bar0.com

- -

Example 577

- -

Backslash-escapes do not work inside email autolinks:

- -

<foo+@bar.example.com>

- -

These are not autolinks:

- -

Example 578

- -

<>

- -

Example 579

- -

< http://foo.bar >

- -

Example 580

- -

<m:abc>

- -

Example 581

- -

<foo.bar.baz>

- -

Example 582

- -

http://example.com

- -

Example 583

- -

foo@bar.example.com

\ No newline at end of file diff --git a/test/new/cm_autolinks.md b/test/new/cm_autolinks.md deleted file mode 100644 index a19d830c9a..0000000000 --- a/test/new/cm_autolinks.md +++ /dev/null @@ -1,96 +0,0 @@ ---- -gfm: false -mangle: false ---- - -Here are some valid autolinks: - -### Example 565 - - - -### Example 566 - - - -### Example 567 - - - -### Example 568 - -Uppercase is also fine: - - - -Note that many strings that count as absolute URIs for purposes of this spec are not valid URIs, because their schemes are not registered or because of other problems with their syntax: - -### Example 569 - - - -### Example 570 - - - -### Example 571 - - - -### Example 572 - - - -### Example 573 - -Spaces are not allowed in autolinks: - - - -### Example 574 - -Backslash-escapes do not work inside autolinks: - - - -Examples of email autolinks: - -### Example 575 - - - -### Example 576 - - - -### Example 577 - -Backslash-escapes do not work inside email autolinks: - - - -These are not autolinks: - -### Example 578 - -<> - -### Example 579 - -< http://foo.bar > - -### Example 580 - - - -### Example 581 - - - -### Example 582 - -http://example.com - -### Example 583 - -foo@bar.example.com \ No newline at end of file diff --git a/test/new/cm_blockquotes.html b/test/new/cm_blockquotes.html deleted file mode 100644 index b4d51b1f6b..0000000000 --- a/test/new/cm_blockquotes.html +++ /dev/null @@ -1,233 +0,0 @@ -

Example 191

- -
-

Foo

-

bar -baz

-
- -

Example 192

- -

The spaces after the > characters can be omitted:

- -
-

Bar

-

bar -baz

-
- -

Example 193

- -

The > characters can be indented 1-3 spaces:

- -
-

Baz

-

bar -baz

-
- -

Example 194

- -

Four spaces gives us a code block:

- -
> # Qux
-> bar
-> baz
- -

Example 195

- -

The Laziness clause allows us to omit the > before paragraph continuation text:

- -
-

Quux

-

bar -baz

-
- -

Example 196

- -

A block quote can contain some lazy and some non-lazy continuation lines:

- -
-

bar -baz -foo

-
- -

Example 197

- -

Laziness only applies to lines that would have been continuations of paragraphs had they been prepended with block quote markers. For example, the > cannot be omitted in the second line of

- -
-

foo

-
-
- -

without changing the meaning.

- -

Example 198

- -
Similarly, if we omit the `>` in the second line then the block quote ends after the first line:
-
-> - foo
-- bar
- -

Example 199

- -

For the same reason, we can’t omit the > in front of subsequent lines of an indented or fenced code block:

- -
-
foo
-
-
bar
- -

Example 200

- -
> ```
-foo
-```
-
-<blockquote>
-<pre><code></code></pre>
-</blockquote>
-<p>foo</p>
-<pre><code></code></pre>
- -

Example 201

-
> foo
-    - bar
-
-<blockquote>
-<p>foo
-- bar</p>
-</blockquote>
- -

Example 202

- -

A block quote can be empty:

- -
-
- -

Example 203

- -
-
- -

Example 204

- -

A block quote can have initial or final blank lines:

- -
-

foo

-
- - -

Example 205

- -

A blank line always separates block quotes:

- -
-

foo

-
-
-

bar

-
- -

Example 206

- -

Consecutiveness means that if we put these block quotes together, we get a single block quote:

- -
-

foo -bar

-
- -

Example 207

- -

To get a block quote with two paragraphs, use:

- -
-

foo

-

bar

-
- -

Example 208

- -

Block quotes can interrupt paragraphs:

- -

foo

-
-

bar

-
- -

Example 209

- -

In general, blank lines are not needed before or after block quotes:

- -
-

aaa

-
-
-
-

bbb

-
- -

Example 210

- -

However, because of laziness, a blank line is needed between a block quote and a following paragraph:

- -
-

bar -baz

-
- -

Example 211

- -
-

bar

-
-

baz

- -

Example 212

- -
-

bar

-
-

baz

- -

Example 213

- -

It is a consequence of the Laziness rule that any number of initial >s may be omitted on a continuation line of a nested block quote:

- -
-
-
-

foo -bar

-
-
-
- -

Example 214

- -
-
-
-

foo -bar -baz

-
-
-
- -

Example 215

- -

When including an indented code block in a block quote, remember that the block quote marker includes both the > and a following space. So five spaces are needed after the >:

- -
-
code
-
-
-

not code

-
diff --git a/test/new/cm_blockquotes.md b/test/new/cm_blockquotes.md deleted file mode 100644 index 6a80a6f32d..0000000000 --- a/test/new/cm_blockquotes.md +++ /dev/null @@ -1,189 +0,0 @@ -### Example 191 - -> # Foo -> bar -> baz - -### Example 192 - -The spaces after the `>` characters can be omitted: - -># Bar ->bar -> baz - -### Example 193 - -The `>` characters can be indented 1-3 spaces: - - > # Baz - > bar - > baz - -### Example 194 - -Four spaces gives us a code block: - - > # Qux - > bar - > baz - -### Example 195 - -The Laziness clause allows us to omit the `>` before paragraph continuation text: - -> # Quux -> bar -baz - -### Example 196 - -A block quote can contain some lazy and some non-lazy continuation lines: - -> bar -baz -> foo - -### Example 197 - -Laziness only applies to lines that would have been continuations of paragraphs had they been prepended with block quote markers. For example, the `>` cannot be omitted in the second line of - -> foo ---- - -without changing the meaning. - -### Example 198 - - Similarly, if we omit the `>` in the second line then the block quote ends after the first line: - - > - foo - - bar - -### Example 199 - -For the same reason, we can’t omit the `>` in front of subsequent lines of an indented or fenced code block: - -> foo - - bar - -### Example 200 - - > ``` - foo - ``` - -
-
-
-

foo

-
- -### Example 201 - - > foo - - bar - -
-

foo - - bar

-
- -### Example 202 - -A block quote can be empty: - -> - -### Example 203 - -> -> -> - -### Example 204 - -A block quote can have initial or final blank lines: - -> -> foo -> - -### Example 205 - -A blank line always separates block quotes: - -> foo - -> bar - -### Example 206 - -Consecutiveness means that if we put these block quotes together, we get a single block quote: - -> foo -> bar - -### Example 207 - -To get a block quote with two paragraphs, use: - -> foo -> -> bar - -### Example 208 - -Block quotes can interrupt paragraphs: - -foo -> bar - -### Example 209 - -In general, blank lines are not needed before or after block quotes: - -> aaa -*** -> bbb - -### Example 210 - -However, because of laziness, a blank line is needed between a block quote and a following paragraph: - -> bar -baz - -### Example 211 - -> bar - -baz - -### Example 212 - -> bar -> -baz - -### Example 213 - -It is a consequence of the Laziness rule that any number of initial `>`s may be omitted on a continuation line of a nested block quote: - -> > > foo -bar - -### Example 214 - ->>> foo -> bar ->>baz - -### Example 215 - -When including an indented code block in a block quote, remember that the block quote marker includes both the `>` and a following space. So five spaces are needed after the `>`: - -> code - -> not code diff --git a/test/new/cm_html_blocks.html b/test/new/cm_html_blocks.html deleted file mode 100644 index 80fdff579f..0000000000 --- a/test/new/cm_html_blocks.html +++ /dev/null @@ -1,300 +0,0 @@ -

HTML blocks

- -

Example 116

- -
-
-**Hello**,
-

world. -

-
- -

Example 117

- - - - - -
- hi -
-

okay.

- -

Example 118

- - -*foo* - -

Example 120

- -
-

Markdown

-
- -

Example 121

- -
-
- -

Example 122

- -
-
- -

Example 123

- -
-*foo* -

bar

- -

Example 124

- -
Example 125 - -
Example 126 - -
Example 127 - - - -

Example 128

- -
-foo -
- -

Example 129

- -
-``` c -int x = 33; -``` - -

Example 130

- - -*bar* - - -

Example 131

- - -*bar* - - -

Example 132

- - -*bar* - - -

Example 133

- - -*bar* - -

Example 134

- - -*foo* - - -

Example 135

- - -

foo

-
- -

Example 136

- -

foo

- -

Example 137

- -

-import Text.HTML.TagSoup
-
-main :: IO ()
-main = print $ parseTags tags
-
-

okay

- -

Example 138

- - -

okay

- -

Example 139

- - -

okay

- -

Example 141

- -
-
-foo -
-

bar

- -

Example 142

- -
    -
  • -
    -
  • -
  • foo
  • -
- -

Example 143

- - -

foo

- -

Example 144

- -*bar* -

baz

- -

Example 145

- -1. *bar* - -

Example 146

- - -

okay

- -

Example 147

- -'; - -?> -

okay

- -

Example 148

- - - -

Example 149

- - -

okay

- -

Example 150

- - -
<!-- foo -->
-
- -

Example 151

- -
-
<div>
-
- -

Example 152

- -

Foo

-
-bar -
- -

Example 153

- -
-bar -
-*foo* - -

Example 154

- -

Foo - -baz

- -

Example 155

- -
-

Emphasized text.

-
- -

Example 156

- -
-*Emphasized* text. -
- -

Example 157

- - - - - -
-Hi -
- -

Example 158

- - - -
<td>
-  Hi
-</td>
-
- -
- -

Example 140

- -

If there is no matching end tag, the block will end at the end of the document (or the enclosing block quote or list item):

- - -okay - -### Example 141 - ->
-> foo - -bar - -### Example 142 - --
-- foo - -### Example 143 - - -*foo* - -### Example 144 - -*bar* -*baz* - -### Example 145 - -1. *bar* - -### Example 146 - - -okay - -### Example 147 - -'; - -?> -okay - -### Example 148 - - - -### Example 149 - - -okay - -### Example 150 - - - - - -### Example 151 - -
- -
- -### Example 152 - -Foo -
-bar -
- -### Example 153 - -
-bar -
-*foo* - -### Example 154 - -Foo -
-baz - -### Example 155 - -
- -*Emphasized* text. - -
- -### Example 156 - -
-*Emphasized* text. -
- -### Example 157 - - - - - - - - - -
-Hi -
- -### Example 158 - - - - - - - - - -
- Hi -
- -### Example 140 - -If there is no matching end tag, the block will end at the end of the document (or the enclosing block quote or list item): - -