From 621f649c2bd5955cc4a5cad3b7a965e55ef6bf8d Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Wed, 13 Mar 2019 08:50:11 -0500 Subject: [PATCH 1/5] abstract htmldiffer --- jasmine.json | 2 +- test/helpers/helpers.js | 24 +++-------------------- test/helpers/html-differ.js | 38 +++++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 22 deletions(-) create mode 100644 test/helpers/html-differ.js diff --git a/jasmine.json b/jasmine.json index 8d1be919d1..bec425427d 100644 --- a/jasmine.json +++ b/jasmine.json @@ -4,7 +4,7 @@ "**/*-spec.js" ], "helpers": [ - "helpers/**/*.js" + "helpers/helpers.js" ], "stopSpecOnExpectationFailure": false, "random": true diff --git a/test/helpers/helpers.js b/test/helpers/helpers.js index c5ef733e91..ea4562e49b 100644 --- a/test/helpers/helpers.js +++ b/test/helpers/helpers.js @@ -1,8 +1,5 @@ const marked = require('../../'); -const HtmlDiffer = require('@markedjs/html-differ').HtmlDiffer; -const htmlDiffer = new HtmlDiffer({ignoreSelfClosingSlash: true}); - -const EXCERPT_LENGTH = 30; +const htmlDiffer = require('./html-differ.js'); beforeEach(() => { marked.setOptions(marked.getDefaults()); @@ -18,23 +15,8 @@ beforeEach(() => { if (result.pass) { result.message = spec.markdown + '\n------\n\nExpected: Should Fail'; } else { - var expectedHtml = expected.replace(/\s/g, ''); - var actualHtml = actual.replace(/\s/g, ''); - - for (var i = 0; i < expectedHtml.length; i++) { - if (actualHtml[i] !== expectedHtml[i]) { - actualHtml = actualHtml.substring( - Math.max(i - EXCERPT_LENGTH, 0), - Math.min(i + EXCERPT_LENGTH, actualHtml.length)); - - expectedHtml = expectedHtml.substring( - Math.max(i - EXCERPT_LENGTH, 0), - Math.min(i + EXCERPT_LENGTH, expectedHtml.length)); - - break; - } - } - result.message = 'Expected:\n' + expectedHtml + '\n\nActual:\n' + actualHtml; + const diff = htmlDiffer.firstDiff(actual, expected); + result.message = 'Expected: ' + diff.expected + '\n Actual: ' + diff.actual; } return result; } diff --git a/test/helpers/html-differ.js b/test/helpers/html-differ.js new file mode 100644 index 0000000000..44052be407 --- /dev/null +++ b/test/helpers/html-differ.js @@ -0,0 +1,38 @@ +const HtmlDiffer = require('@markedjs/html-differ').HtmlDiffer; +const htmlDiffer = new HtmlDiffer({ignoreSelfClosingSlash: true}); + +module.exports = { + isEqual: htmlDiffer.isEqual.bind(htmlDiffer), + firstDiff: (actual, expected, padding) => { + padding = padding || 30; + const result = htmlDiffer + .diffHtml(actual, expected) + .reduce((obj, diff) => { + if (diff.added) { + if (obj.firstIndex === null) { + obj.firstIndex = obj.expected.length; + } + obj.expected += diff.value; + } else if (diff.removed) { + if (obj.firstIndex === null) { + obj.firstIndex = obj.actual.length; + } + obj.actual += diff.value; + } else { + obj.actual += diff.value; + obj.expected += diff.value; + } + + return obj; + }, { + firstIndex: null, + actual: '', + expected: '' + }); + + return { + actual: result.actual.substring(result.firstIndex - padding, result.firstIndex + padding), + expected: result.expected.substring(result.firstIndex - padding, result.firstIndex + padding) + }; + } +}; From da57301be8945e99e569a856bd9422296e3f84af Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Wed, 13 Mar 2019 08:50:53 -0500 Subject: [PATCH 2/5] use htmldiffer in file tests & update to node 4 --- test/index.js | 259 ++++++++++++++++++++------------------------------ 1 file changed, 104 insertions(+), 155 deletions(-) diff --git a/test/index.js b/test/index.js index 7e8f5e3af3..27bdea66e1 100644 --- a/test/index.js +++ b/test/index.js @@ -1,4 +1,6 @@ #!/usr/bin/env node +'use strict'; +// 'use strict' is here so we can use let and const in node 4 /** * marked tests @@ -10,12 +12,12 @@ * Modules */ -var fs = require('fs'), - path = require('path'), - fm = require('front-matter'), - g2r = require('glob-to-regexp'), - marked = require('../'), - markedMin = require('../marked.min.js'); +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 @@ -23,44 +25,36 @@ var fs = require('fs'), function load(options) { options = options || {}; - var dir = path.join(__dirname, 'compiled_tests'), - files = {}, - list, - file, - name, - content, - glob = g2r(options.glob || '*', { extended: true }), - i, - l; - - list = fs + const dir = path.join(__dirname, 'compiled_tests'); + const glob = g2r(options.glob || '*', { extended: true }); + + const list = fs .readdirSync(dir) - .filter(function(file) { + .filter(file => { return path.extname(file) === '.md'; }) .sort(); - l = list.length; - - for (i = 0; i < l; i++) { - name = path.basename(list[i], '.md'); + const files = list.reduce((obj, item) => { + const name = path.basename(item, '.md'); if (glob.test(name)) { - file = path.join(dir, list[i]); - content = fm(fs.readFileSync(file, 'utf8')); + const file = path.join(dir, item); + const content = fm(fs.readFileSync(file, 'utf8')); - files[name] = { + 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(function(name) { + fs.readdirSync(path.join(__dirname, 'new')).forEach(name => { if (path.extname(name) === '.html') return; if (name === 'main.md') return; delete files[name]; @@ -93,25 +87,21 @@ function runTests(engine, options) { engine = engine || marked; options = options || {}; - var succeeded = 0, - failed = 0, - files = options.files || load(options), - filenames = Object.keys(files), - len = filenames.length, - success, - i, - filename, - file; + + 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 (i = 0; i < len; i++) { - filename = filenames[i]; - file = files[filename]; + for (let i = 0; i < filenames.length; i++) { + const filename = filenames[i]; + const file = files[filename]; - success = testFile(engine, file, filename, i + 1); + const success = testFile(engine, file, filename, i + 1); if (success) { succeeded++; @@ -123,8 +113,8 @@ function runTests(engine, options) { } } - console.log('%d/%d tests completed successfully.', succeeded, len); - if (failed) console.log('%d/%d tests failed.', failed, len); + console.log('\n%d/%d tests completed successfully.', succeeded, filenames.length); + if (failed) console.log('%d/%d tests failed.', failed, filenames.length); return !failed; } @@ -134,13 +124,7 @@ function runTests(engine, options) { */ function testFile(engine, file, filename, index) { - var opts = Object.keys(file.options), - text, - html, - j, - l, - before, - elapsed; + const opts = Object.keys(file.options); if (marked._original) { marked.defaults = marked._original; @@ -152,91 +136,63 @@ function testFile(engine, file, filename, index) { if (opts.length) { marked._original = marked.defaults; marked.defaults = {}; - Object.keys(marked._original).forEach(function(key) { + Object.keys(marked._original).forEach(key => { marked.defaults[key] = marked._original[key]; }); - opts.forEach(function(key) { + opts.forEach(key => { if (marked.defaults.hasOwnProperty(key)) { marked.defaults[key] = file.options[key]; } }); } - before = process.hrtime(); + const before = process.hrtime(); + + let text, html, elapsed; try { - text = engine(file.text).replace(/\s/g, ''); - html = file.html.replace(/\s/g, ''); + text = engine(file.text); + html = file.html; } catch (e) { elapsed = process.hrtime(before); - console.log(' failed in %dms', prettyElapsedTime(elapsed)); + console.log('\n failed in %dms\n', prettyElapsedTime(elapsed)); throw e; } elapsed = process.hrtime(before); - l = html.length; - - if (l === 0 && text.length > 0) { - text = text.substring(0, Math.min(30, text.length)); - - console.log(' failed in %dms at offset %d. Near: "%s".\n', prettyElapsedTime(elapsed), 0, text); - - console.log('\nActual:\n%s\n', text.trim() || text); - console.log('\nExpected:\n\n'); - - return false; - } - - for (j = 0; j < l; j++) { - if (text[j] !== html[j]) { - text = text.substring( - Math.max(j - 30, 0), - Math.min(j + 30, text.length)); - - html = html.substring( - Math.max(j - 30, 0), - Math.min(j + 30, l)); - - console.log(' failed in %dms at offset %d. Near: "%s".\n', prettyElapsedTime(elapsed), j, text); - - console.log('\nActual:\n%s\n', text.trim() || text); - console.log('\nExpected:\n%s\n', html.trim() || html); - + 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; } - if (elapsed[0] > 0) { - console.log(' failed because it took too long.\n\n passed in %dms', prettyElapsedTime(elapsed)); - return false; - } + const diff = htmlDiffer.firstDiff(text, html); - console.log(' passed in %dms', prettyElapsedTime(elapsed)); - return true; + 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, func) { - var start = Date.now(), - times = 1000, - keys = Object.keys(files), - i, - l = keys.length, - filename, - file; - - while (times--) { - for (i = 0; i < l; i++) { - filename = keys[i]; - file = files[filename]; - func(file.text); +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); } } - console.log('%s completed in %dms.', name, Date.now() - start); + const end = Date.now(); + + console.log('%s completed in %dms.', name, end - start); } /** @@ -245,7 +201,7 @@ function bench(name, files, func) { function runBench(options) { options = options || {}; - var files = load(options); + const files = load(options); // Non-GFM, Non-pedantic marked.setOptions({ @@ -289,13 +245,13 @@ function runBench(options) { } bench('marked (pedantic)', files, marked); - // showdown + // commonmark try { - bench('commonmark', files, (function() { - var commonmark = require('commonmark'), - parser = new commonmark.Parser(), - writer = new commonmark.HtmlRenderer(); - return function(text) { + 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)); }; })()); @@ -305,12 +261,10 @@ function runBench(options) { // markdown-it try { - bench('markdown-it', files, (function() { - var MarkdownIt = require('markdown-it'); - var md = new MarkdownIt(); - return function(text) { - return md.render(text); - }; + 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); @@ -318,11 +272,9 @@ function runBench(options) { // markdown.js try { - bench('markdown.js', files, (function() { - var markdown = require('markdown').markdown; - return function(text) { - return markdown.toHTML(text); - }; + 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); @@ -337,7 +289,7 @@ function runBench(options) { function time(options) { options = options || {}; - var files = load(options); + const files = load(options); if (options.marked) { marked.setOptions(options.marked); } @@ -357,7 +309,7 @@ function time(options) { */ function fix() { - ['compiled_tests', 'original', 'new', 'redos'].forEach(function(dir) { + ['compiled_tests', 'original', 'new', 'redos'].forEach(dir => { try { fs.mkdirSync(path.resolve(__dirname, dir)); } catch (e) { @@ -366,13 +318,13 @@ function fix() { }); // rm -rf tests - fs.readdirSync(path.resolve(__dirname, 'compiled_tests')).forEach(function(file) { + 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(function(file) { - var text = fs.readFileSync(path.resolve(__dirname, 'original', file), 'utf8'); + 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)) { @@ -387,13 +339,13 @@ function fix() { }); // node fix.js - var dir = path.join(__dirname, 'compiled_tests'); + const dir = path.join(__dirname, 'compiled_tests'); - fs.readdirSync(dir).filter(function(file) { + fs.readdirSync(dir).filter(file => { return path.extname(file) === '.html'; - }).forEach(function(file) { + }).forEach(file => { file = path.join(dir, file); - var html = fs.readFileSync(file, 'utf8'); + let html = fs.readFileSync(file, 'utf8'); // fix unencoded quotes html = html @@ -408,9 +360,9 @@ function fix() { }); // turn
into
- fs.readdirSync(dir).forEach(function(file) { + fs.readdirSync(dir).forEach(file => { file = path.join(dir, file); - var text = fs.readFileSync(file, 'utf8'); + let text = fs.readFileSync(file, 'utf8'); text = text.replace(/(<|<)hr\s*\/(>|>)/g, '$1hr$2'); @@ -419,22 +371,22 @@ function fix() { // 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') + { + 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(function(file) { + 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(function(file) { + fs.readdirSync(path.resolve(__dirname, 'redos')).forEach(file => { fs.writeFileSync(path.resolve(__dirname, 'compiled_tests', file), fs.readFileSync(path.resolve(__dirname, 'redos', file))); }); @@ -445,14 +397,13 @@ function fix() { */ function parseArg(argv) { - var options = {}, - opt = '', - orphans = [], - arg; - argv = argv.slice(2); + + const options = {}; + const orphans = []; + function getarg() { - var arg = argv.shift(); + let arg = argv.shift(); if (arg.indexOf('--') === 0) { // e.g. --opt @@ -465,7 +416,7 @@ function parseArg(argv) { } else if (arg[0] === '-') { if (arg.length > 2) { // e.g. -abc - argv = arg.substring(1).split('').map(function(ch) { + argv = arg.substring(1).split('').map(ch => { return '-' + ch; }).concat(argv); arg = argv.shift(); @@ -480,7 +431,7 @@ function parseArg(argv) { } while (argv.length) { - arg = getarg(); + let arg = getarg(); switch (arg) { case '-f': case '--fix': @@ -515,7 +466,7 @@ function parseArg(argv) { break; default: if (arg.indexOf('--') === 0) { - opt = camelize(arg.replace(/^--(no-)?/, '')); + const opt = camelize(arg.replace(/^--(no-)?/, '')); if (!marked.defaults.hasOwnProperty(opt)) { continue; } @@ -544,9 +495,7 @@ function parseArg(argv) { */ function camelize(text) { - return text.replace(/(\w)-(\w)/g, function(_, a, b) { - return a + b.toUpperCase(); - }); + return text.replace(/(\w)-(\w)/g, (_, a, b) => a + b.toUpperCase()); } /** @@ -554,7 +503,7 @@ function camelize(text) { */ function main(argv) { - var opt = parseArg(argv); + const opt = parseArg(argv); if (opt.fix !== false) { fix(); @@ -574,7 +523,7 @@ function main(argv) { } if (opt.minified) { - marked = markedMin; + marked = require('../marked.min.js'); } return runTests(opt); } @@ -599,7 +548,7 @@ if (!module.parent) { // returns time to millisecond granularity function prettyElapsedTime(hrtimeElapsed) { - var seconds = hrtimeElapsed[0]; - var frac = Math.round(hrtimeElapsed[1] / 1e3) / 1e3; + const seconds = hrtimeElapsed[0]; + const frac = Math.round(hrtimeElapsed[1] / 1e3) / 1e3; return seconds * 1e3 + frac; } From a16251dd42d6af59d05009c6782cc6f0cfaa90f2 Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Wed, 13 Mar 2019 08:51:07 -0500 Subject: [PATCH 3/5] fix test spacing --- test/new/uppercase_hex.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/new/uppercase_hex.html b/test/new/uppercase_hex.html index aee191ff19..97f55535c6 100644 --- a/test/new/uppercase_hex.html +++ b/test/new/uppercase_hex.html @@ -1,2 +1,2 @@ -

lowerclickmelower -upperclickmeupper

+

lowerclick melower +upperclick meupper

From ff127c5a5952aecf0fd8ac705b4ea60688f34ecd Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Wed, 13 Mar 2019 10:39:01 -0500 Subject: [PATCH 4/5] use template literals --- test/helpers/helpers.js | 4 ++-- test/index.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/helpers/helpers.js b/test/helpers/helpers.js index ea4562e49b..44632fcda9 100644 --- a/test/helpers/helpers.js +++ b/test/helpers/helpers.js @@ -13,10 +13,10 @@ beforeEach(() => { result.pass = htmlDiffer.isEqual(expected, actual); if (result.pass) { - result.message = spec.markdown + '\n------\n\nExpected: Should Fail'; + result.message = `${spec.markdown}\n------\n\nExpected: Should Fail`; } else { const diff = htmlDiffer.firstDiff(actual, expected); - result.message = 'Expected: ' + diff.expected + '\n Actual: ' + diff.actual; + result.message = `Expected: ${diff.expected}\n Actual: ${diff.actual}`; } return result; } diff --git a/test/index.js b/test/index.js index 27bdea66e1..73529468f8 100644 --- a/test/index.js +++ b/test/index.js @@ -329,9 +329,9 @@ function fix() { if (path.extname(file) === '.md') { if (fm.test(text)) { text = fm(text); - text = '---\n' + text.frontmatter + '\ngfm: false\n---\n' + text.body; + text = `---\n${text.frontmatter}\ngfm: false\n---\n${text.body}`; } else { - text = '---\ngfm: false\n---\n' + text; + text = `---\ngfm: false\n---\n${text}`; } } @@ -372,7 +372,7 @@ function fix() { // markdown does some strange things. // it does not encode naked `>`, marked does. { - const file = dir + '/amps_and_angles_encoding.html'; + const file = `${dir}/amps_and_angles_encoding.html`; const html = fs.readFileSync(file, 'utf8') .replace('6 > 5.', '6 > 5.'); @@ -417,7 +417,7 @@ function parseArg(argv) { if (arg.length > 2) { // e.g. -abc argv = arg.substring(1).split('').map(ch => { - return '-' + ch; + return `-${ch}`; }).concat(argv); arg = argv.shift(); } else { From 0cd033361f44f2c8d0ae7a6ffc01870cf2b8131d Mon Sep 17 00:00:00 2001 From: Tony Brix Date: Wed, 13 Mar 2019 10:40:51 -0500 Subject: [PATCH 5/5] remove redundant comments --- test/index.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/index.js b/test/index.js index 73529468f8..5027ee5b90 100644 --- a/test/index.js +++ b/test/index.js @@ -245,7 +245,6 @@ function runBench(options) { } bench('marked (pedantic)', files, marked); - // commonmark try { bench('commonmark', files, (() => { const commonmark = require('commonmark'); @@ -259,7 +258,6 @@ function runBench(options) { console.log('Could not bench commonmark. (Error: %s)', e.message); } - // markdown-it try { bench('markdown-it', files, (() => { const MarkdownIt = require('markdown-it'); @@ -270,7 +268,6 @@ function runBench(options) { console.log('Could not bench markdown-it. (Error: %s)', e.message); } - // markdown.js try { bench('markdown.js', files, (() => { const markdown = require('markdown').markdown;