markdown-it-include
Version:
Markdown-it plugin which adds the ability to include markdown fragment files.
177 lines (142 loc) • 5.81 kB
JavaScript
/* eslint-env mocha, es6 */
/*eslint-env mocha*/
const assert = require('chai').assert;
const path = require('path');
const generate = require('markdown-it-testgen');
const markdown = require('markdown-it');
const markdown_it_include = require('../');
const fixturesPath = path.join(__dirname, 'fixtures');
describe('plugin', () => {
describe('right workflows', () => {
it ('default.txt', () => {
const md = markdown()
.use(markdown_it_include, fixturesPath);
generate(path.join(__dirname, 'fixtures/default.txt'), md);
});
it ('including same field twice', () => {
const md = markdown()
.use(markdown_it_include, fixturesPath);
assert.equal(md.render('!!! include( a.md ) !!!\n!!! include( a.md ) !!!'),
'<p><em>a content</em>\n<em>a content</em></p>\n');
});
it ('default options', () => {
let md = markdown()
.use(markdown_it_include);
assert.equal(md.render('!!! include( test/fixtures/a.md ) !!!\n'),
'<p><em>a content</em></p>\n');
md = markdown()
.use(markdown_it_include, {});
assert.equal(md.render('!!! include( test/fixtures/a.md ) !!!\n'),
'<p><em>a content</em></p>\n');
});
it ('root option', () => {
const md = markdown()
.use(markdown_it_include, { root: fixturesPath });
assert.equal(md.render('!!! include( a.md ) !!!\n'),
'<p><em>a content</em></p>\n');
});
it ('includeRe option', () => {
const md = markdown()
.use(markdown_it_include, {
root: fixturesPath,
includeRe: /<\[include\](.+)/i,
bracesAreOptional: false // path MUST have braces
});
assert.equal(md.render('<[include]( a.md )\n'),
'<p><em>a content</em></p>\n');
});
});
describe('wrong workflows', () => {
it ('file not found', () => {
const md = markdown()
.use(markdown_it_include, fixturesPath);
assert.throws(
() => md.render('!!! include( xxx.md ) !!!'),
Error, /not found/i
);
});
it ('direct circular reference', () => {
const md = markdown()
.use(markdown_it_include, fixturesPath);
assert.throws(
() => md.render('!!! include( c.md ) !!!'),
Error, /circular reference/i
);
});
it ('indirect circular reference', () => {
const md = markdown()
.use(markdown_it_include, fixturesPath);
assert.throws(
() => md.render('!!! include( L1/L2/e2.md ) !!!'),
Error, /circular reference/i
);
});
});
describe('options', () => {
const options = {
root: fixturesPath,
includeRe: /#include(.+)/,
bracesAreOptional: true
};
it ('accepts C-like includes with custom RE', () => {
const md = markdown()
.use(markdown_it_include, options);
generate(path.join(__dirname, 'fixtures/incM.txt'), md);
});
it ('barfs on illegal include statement without space(s)', () => {
const md = markdown()
.use(markdown_it_include, options);
assert.throws(
() => md.render('#includexxx.md'),
Error, /when not using braces around the path.*it MUST be preceeded by at least one whitespace character/i
);
});
it ('barfs on missing braces when option says they\'re mandatory (default)', () => {
const md = markdown()
.use(markdown_it_include, { ...options, bracesAreOptional: false });
assert.throws(
() => md.render('#include xxx.md'),
Error, /MUST have .*().* braces around the include path/i
);
});
it ('dumps error in the generated output when throwError option is FALSE', () => {
const md = markdown()
.use(markdown_it_include, { ...options, throwError: false });
assert.match(md.render('#include(xxx.md)'),
/<h1>INCLUDE ERROR: File .*xxx\.md.* not found\.<\/h1>/i);
});
// same example code as in the README for getRootDir, adjusted for the test directories:
it ('accepts a dynamic option.getRootDir() function to load includes from active directory', () => {
const options2 = {
root: '/bogus/', // this is not used here.
getRootDir: (pluginOptions, state, startLine, endLine) => state.env.getIncludeRootDir(pluginOptions, state, startLine, endLine),
includeRe: /#include(.+)/,
bracesAreOptional: true
};
const md = markdown()
.use(markdown_it_include, options2);
// `mdPath` is an absolute path assumed to be pointing to the MD file being processed:
const mdPath = path.resolve(path.join(__dirname, 'fixtures/foo/bar.md'));
let env = {};
env.getIncludeRootDir = () => path.dirname(mdPath);
// Use the 'unwrapped' version of the md.render / md.parse process:
// ----------------------------------------------------------------
//
// let content = md.render(data); --> .parse + .renderer.render
//
// .parse --> new state + process: return tokens
// let tokens = md.parse(data, env)
/* eslint max-len: "off" */
let state = new md.core.State('bla\n\n#include(../z.md)\n\n#include(../q.md)\n', md, env); // <-- here our env is injected into state!
md.core.process(state);
let tokens = state.tokens;
// now call md.render():
let htmlContent = md.renderer.render(tokens, md.options, env);
// presto! (End of `env` lifetime, BTW.)
// What happened above? q.md includes a1.md and a2.md:
assert.equal(htmlContent,
/* eslint max-len: "off" */
'<p>bla</p>\n<p>z content*</p>\n<p>*q content & checking nested includes next:</p>\n<p><em>a1 content</em>\n<em>a2 content</em></p>\n');
});
});
});