'use strict'; var assert = require('chai').assert; var markdownit = require('../'); describe('API', function () { it('constructor', function () { assert.throws(function () { markdownit('bad preset'); }); // options should override preset var md = markdownit('commonmark', { html: false }); assert.strictEqual(md.render(''), '

<!-- -->

\n'); }); it('configure coverage', function () { var md = markdownit(); // conditions coverage md.configure({}); assert.strictEqual(md.render('123'), '

123

\n'); assert.throws(function () { md.configure(); }); }); it('plugin', function () { var succeeded = false; function plugin(slf, opts) { if (opts === 'bar') { succeeded = true; } } var md = markdownit(); md.use(plugin, 'foo'); assert.strictEqual(succeeded, false); md.use(plugin, 'bar'); assert.strictEqual(succeeded, true); }); it('highlight', function () { var md = markdownit({ highlight: function (str) { return '
==' + str + '==
'; } }); assert.strictEqual(md.render('```\nhl\n```'), '
==hl\n==
\n'); }); it('highlight escape by default', function () { var md = markdownit({ highlight: function () { return ''; } }); assert.strictEqual(md.render('```\n&\n```'), '
&\n
\n'); }); it('highlight arguments', function () { var md = markdownit({ highlight: function (str, lang, attrs) { assert.strictEqual(lang, 'a'); assert.strictEqual(attrs, 'b c d'); return '
==' + str + '==
'; } }); assert.strictEqual(md.render('``` a b c d \nhl\n```'), '
==hl\n==
\n'); }); it('force hardbreaks', function () { var md = markdownit({ breaks: true }); assert.strictEqual(md.render('a\nb'), '

a
\nb

\n'); md.set({ xhtmlOut: true }); assert.strictEqual(md.render('a\nb'), '

a
\nb

\n'); }); it('xhtmlOut enabled', function () { var md = markdownit({ xhtmlOut: true }); assert.strictEqual(md.render('---'), '
\n'); assert.strictEqual(md.render('![]()'), '

\n'); assert.strictEqual(md.render('a \\\nb'), '

a
\nb

\n'); }); it('xhtmlOut disabled', function () { var md = markdownit(); assert.strictEqual(md.render('---'), '
\n'); assert.strictEqual(md.render('![]()'), '

\n'); assert.strictEqual(md.render('a \\\nb'), '

a
\nb

\n'); }); it('bulk enable/disable rules in different chains', function () { var md = markdownit(); var was = { core: md.core.ruler.getRules('').length, block: md.block.ruler.getRules('').length, inline: md.inline.ruler.getRules('').length }; // Disable 2 rule in each chain & compare result md.disable([ 'block', 'inline', 'code', 'fence', 'emphasis', 'entity' ]); var now = { core: md.core.ruler.getRules('').length + 2, block: md.block.ruler.getRules('').length + 2, inline: md.inline.ruler.getRules('').length + 2 }; assert.deepEqual(was, now); // Enable the same rules back md.enable([ 'block', 'inline', 'code', 'fence', 'emphasis', 'entity' ]); var back = { core: md.core.ruler.getRules('').length, block: md.block.ruler.getRules('').length, inline: md.inline.ruler.getRules('').length }; assert.deepEqual(was, back); }); it('bulk enable/disable with errors control', function () { var md = markdownit(); assert.throws(function () { md.enable([ 'link', 'code', 'invalid' ]); }); assert.throws(function () { md.disable([ 'link', 'code', 'invalid' ]); }); assert.doesNotThrow(function () { md.enable([ 'link', 'code' ]); }); assert.doesNotThrow(function () { md.disable([ 'link', 'code' ]); }); }); it('bulk enable/disable should understand strings', function () { var md = markdownit(); md.disable('emphasis'); assert(md.renderInline('_foo_'), '_foo_'); md.enable('emphasis'); assert(md.renderInline('_foo_'), 'foo'); }); it('input type check', function () { var md = markdownit(); assert.throws( function () { md.render(null); }, /Input data should be a String/ ); }); }); describe('Plugins', function () { it('should not loop infinitely if all rules are disabled', function () { var md = markdownit(); md.inline.ruler.enableOnly([]); md.inline.ruler2.enableOnly([]); md.block.ruler.enableOnly([]); assert.throws(() => md.render(' - *foo*\n - `bar`'), /none of the block rules matched/); }); it('should not loop infinitely if inline rule doesn\'t increment pos', function () { var md = markdownit(); md.inline.ruler.after('text', 'custom', function (state/*, silent*/) { if (state.src.charCodeAt(state.pos) !== 0x40/* @ */) return false; return true; }); assert.throws(() => md.render('foo@bar'), /inline rule didn't increment state.pos/); assert.throws(() => md.render('[foo@bar]()'), /inline rule didn't increment state.pos/); }); it('should not loop infinitely if block rule doesn\'t increment pos', function () { var md = markdownit(); md.block.ruler.before('paragraph', 'custom', function (state, startLine/*, endLine, silent*/) { var pos = state.bMarks[startLine] + state.tShift[startLine]; if (state.src.charCodeAt(pos) !== 0x40/* @ */) return false; return true; }, { alt: [ 'paragraph' ] }); assert.throws(() => md.render('foo\n@bar\nbaz'), /block rule didn't increment state.line/); assert.throws(() => md.render('foo\n\n@bar\n\nbaz'), /block rule didn't increment state.line/); }); }); describe('Misc', function () { it('Should replace NULL characters', function () { var md = markdownit(); assert.strictEqual(md.render('foo\u0000bar'), '

foo\uFFFDbar

\n'); }); it('Should correctly parse strings without tailing \\n', function () { var md = markdownit(); assert.strictEqual(md.render('123'), '

123

\n'); assert.strictEqual(md.render('123\n'), '

123

\n'); assert.strictEqual(md.render(' codeblock'), '
codeblock\n
\n'); assert.strictEqual(md.render(' codeblock\n'), '
codeblock\n
\n'); }); it('Should quickly exit on empty string', function () { var md = markdownit(); assert.strictEqual(md.render(''), ''); }); it('Should parse inlines only', function () { var md = markdownit(); assert.strictEqual(md.renderInline('a *b* c'), 'a b c'); }); it('Renderer should have pluggable inline and block rules', function () { var md = markdownit(); md.renderer.rules.em_open = function () { return ''; }; md.renderer.rules.em_close = function () { return ''; }; md.renderer.rules.paragraph_open = function () { return ''; }; md.renderer.rules.paragraph_close = function () { return ''; }; assert.strictEqual(md.render('*b*'), 'b'); }); it('Zero preset should disable everything', function () { var md = markdownit('zero'); assert.strictEqual(md.render('___foo___'), '

___foo___

\n'); assert.strictEqual(md.renderInline('___foo___'), '___foo___'); md.enable('emphasis'); assert.strictEqual(md.render('___foo___'), '

foo

\n'); assert.strictEqual(md.renderInline('___foo___'), 'foo'); }); it('Should correctly check block termination rules when those are disabled (#13)', function () { var md = markdownit('zero'); assert.strictEqual(md.render('foo\nbar'), '

foo\nbar

\n'); }); it('Should render link target attr', function () { var md = markdownit() .use(require('markdown-it-for-inline'), 'target', 'link_open', function (tokens, idx) { tokens[idx].attrs.push([ 'target', '_blank' ]); }); assert.strictEqual(md.render('[foo](bar)'), '

foo

\n'); }); it('Should normalize CR to LF', function () { var md = markdownit(); assert.strictEqual( md.render('# test\r\r - hello\r - world\r'), md.render('# test\n\n - hello\n - world\n') ); }); it('Should normalize CR+LF to LF', function () { var md = markdownit(); assert.strictEqual( md.render('# test\r\n\r\n - hello\r\n - world\r\n'), md.render('# test\n\n - hello\n - world\n') ); }); it('Should escape surrogate pairs (coverage)', function () { var md = markdownit(); assert.strictEqual(md.render('\\\uD835\uDC9C'), '

\\\uD835\uDC9C

\n'); assert.strictEqual(md.render('\\\uD835x'), '

\\\uD835x

\n'); assert.strictEqual(md.render('\\\uD835'), '

\\\uD835

\n'); }); }); describe('Url normalization', function () { it('Should be overridable', function () { var md = markdownit({ linkify: true }); md.normalizeLink = function (url) { assert(url.match(/example\.com/), 'wrong url passed'); return 'LINK'; }; md.normalizeLinkText = function (url) { assert(url.match(/example\.com/), 'wrong url passed'); return 'TEXT'; }; assert.strictEqual(md.render('foo@example.com'), '

TEXT

\n'); assert.strictEqual(md.render('http://example.com'), '

TEXT

\n'); assert.strictEqual(md.render(''), '

TEXT

\n'); assert.strictEqual(md.render(''), '

TEXT

\n'); assert.strictEqual(md.render('[test](http://example.com)'), '

test

\n'); assert.strictEqual(md.render('![test](http://example.com)'), '

test

\n'); }); }); describe('Links validation', function () { it('Override validator, disable everything', function () { var md = markdownit({ linkify: true }); md.validateLink = function () { return false; }; assert.strictEqual(md.render('foo@example.com'), '

foo@example.com

\n'); assert.strictEqual(md.render('http://example.com'), '

http://example.com

\n'); assert.strictEqual(md.render(''), '

<foo@example.com>

\n'); assert.strictEqual(md.render(''), '

<http://example.com>

\n'); assert.strictEqual(md.render('[test](http://example.com)'), '

[test](http://example.com)

\n'); assert.strictEqual(md.render('![test](http://example.com)'), '

![test](http://example.com)

\n'); }); }); describe('maxNesting', function () { it('Block parser should not nest above limit', function () { var md = markdownit({ maxNesting: 2 }); assert.strictEqual( md.render('>foo\n>>bar\n>>>baz'), '
\n

foo

\n
\n
\n' ); }); it('Inline parser should not nest above limit', function () { var md = markdownit({ maxNesting: 1 }); assert.strictEqual( md.render('[`foo`]()'), '

`foo`

\n' ); }); it('Inline nesting coverage', function () { var md = markdownit({ maxNesting: 2 }); assert.strictEqual( md.render('[[[[[[[[[[[[[[[[[[foo]()'), '

[[[[[[[[[[[[[[[[[[foo]()

\n' ); }); }); describe('smartquotes', function () { var md = markdownit({ typographer: true, // all strings have different length to make sure // we didn't accidentally count the wrong one quotes: [ '[[[', ']]', '(((((', '))))' ] }); it('Should support multi-character quotes', function () { assert.strictEqual( md.render('"foo" \'bar\''), '

[[[foo]] (((((bar))))

\n' ); }); it('Should support nested multi-character quotes', function () { assert.strictEqual( md.render('"foo \'bar\' baz"'), '

[[[foo (((((bar)))) baz]]

\n' ); }); it('Should support multi-character quotes in different tags', function () { assert.strictEqual( md.render('"a *b \'c *d* e\' f* g"'), '

[[[a b (((((c d e)))) f g]]

\n' ); }); }); describe('Ordered list info', function () { var md = markdownit(); function type_filter(tokens, type) { return tokens.filter(function (t) { return t.type === type; }); } it('Should mark ordered list item tokens with info', function () { var tokens = md.parse('1. Foo\n2. Bar\n20. Fuzz'); assert.strictEqual(type_filter(tokens, 'ordered_list_open').length, 1); tokens = type_filter(tokens, 'list_item_open'); assert.strictEqual(tokens.length, 3); assert.strictEqual(tokens[0].info, '1'); assert.strictEqual(tokens[0].markup, '.'); assert.strictEqual(tokens[1].info, '2'); assert.strictEqual(tokens[1].markup, '.'); assert.strictEqual(tokens[2].info, '20'); assert.strictEqual(tokens[2].markup, '.'); tokens = md.parse(' 1. Foo\n2. Bar\n 20. Fuzz\n 199. Flp'); assert.strictEqual(type_filter(tokens, 'ordered_list_open').length, 1); tokens = type_filter(tokens, 'list_item_open'); assert.strictEqual(tokens.length, 4); assert.strictEqual(tokens[0].info, '1'); assert.strictEqual(tokens[0].markup, '.'); assert.strictEqual(tokens[1].info, '2'); assert.strictEqual(tokens[1].markup, '.'); assert.strictEqual(tokens[2].info, '20'); assert.strictEqual(tokens[2].markup, '.'); assert.strictEqual(tokens[3].info, '199'); assert.strictEqual(tokens[3].markup, '.'); }); }); describe('Token attributes', function () { it('.attrJoin', function () { var md = markdownit(); var tokens = md.parse('```'), t = tokens[0]; t.attrJoin('class', 'foo'); t.attrJoin('class', 'bar'); assert.strictEqual( md.renderer.render(tokens, md.options), '
\n' ); }); it('.attrSet', function () { var md = markdownit(); var tokens = md.parse('```'), t = tokens[0]; t.attrSet('class', 'foo'); assert.strictEqual( md.renderer.render(tokens, md.options), '
\n' ); t.attrSet('class', 'bar'); assert.strictEqual( md.renderer.render(tokens, md.options), '
\n' ); }); it('.attrGet', function () { var md = markdownit(); var tokens = md.parse('```'), t = tokens[0]; assert.strictEqual(t.attrGet('myattr'), null); t.attrSet('myattr', 'myvalue'); assert.strictEqual(t.attrGet('myattr'), 'myvalue'); }); });