const { expect } = require('chai'); const unified = require('unified'); const markdown = require('remark-parse'); const mdStringify = require('remark-html'); const { remarkExtend } = require('../src/remarkExtend.js'); /** * @param {function} method * @param {object} options * @param {string} [options.errorMatch] * @param {string} [options.errorMessage] */ async function expectThrowsAsync(method, { errorMatch, errorMessage } = {}) { let error = null; try { await method(); } catch (err) { error = err; } expect(error).to.be.an('Error', 'No error was thrown'); if (errorMatch) { expect(error.message).to.match(errorMatch); } if (errorMessage) { expect(error.message).to.equal(errorMessage); } } async function execute(input, { globalReplaceFunction } = {}) { const parser = unified() // .use(markdown) .use(remarkExtend, { rootDir: __dirname, page: { inputPath: 'test-file.md' }, globalReplaceFunction, }) .use(mdStringify); const result = await parser.process(input); return result.contents; } describe('remarkExtend', () => { it('does no modifications if no action is found', async () => { const result = await execute( ['### Red', 'red is the fire', '#### More Red', 'the sun can get red'].join('\n'), ); expect(result).to.equal( [ '

Red

', '

red is the fire

', '

More Red

', '

the sun can get red

', '', ].join('\n'), ); }); it('can import another file', async () => { const result = await execute( [ // '### Red', "```js ::import('./fixtures/import-me.md')", '```', ].join('\n'), ); expect(result).to.equal(['

Red

', '

import me headline

', ''].join('\n')); }); it('will rewrite image urls/paths but not links', async () => { const result = await execute( [ // '### Static Headline', "```js ::import('./fixtures/import-with-image.md')", '```', ].join('\n'), ); expect(result.replace('\r', '')).to.equal( [ '

Static Headline

', '

import me headline

', '

my image', 'link to

', '', ].join('\n'), ); }); it('can import another file from a start point', async () => { const result = await execute( [ // '# Static Headline', "```js ::import('./fixtures/three-sections-red.md', 'heading:has([value=More Red])')", '```', ].join('\n'), ); expect(result).to.equal( [ '

Static Headline

', '

More Red

', '

the sun can get red

', '

Additional Red

', '

the red sea

', '', ].join('\n'), ); }); it('can import another file from a start to an end point', async () => { const result = await execute( [ // '# Red', "```js ::import('./fixtures/three-sections-red.md', 'heading:has([value=More Red])', 'heading:has([value=More Red]) ~ heading[depth=2]')", '```', ].join('\n'), ); expect(result).to.equal( ['

Red

', '

More Red

', '

the sun can get red

', ''].join('\n'), ); }); it('can do replacements on imports', async () => { const result = await execute( [ // '# Red', "```js ::import('./fixtures/three-sections-red.md', 'heading:has([value=More Red])', 'heading:has([value=More Red]) ~ heading[depth=2]')", 'module.exports.replaceSection = (node) => {', ' if (node.value) {', " node.value = node.value.replace(/red/g, 'green').replace(/Red/g, 'Green');", ' }', ' return node;', '};', '```', ].join('\n'), ); expect(result).to.equal( ['

Red

', '

More Green

', '

the sun can get green

', ''].join('\n'), ); }); it('can do global replacements, that run before local replacements', async () => { const result = await execute( [ // '# Red', "```js ::import('./fixtures/three-sections-red.md', 'heading:has([value=More Red])', 'heading:has([value=More Red]) ~ heading[depth=2]')", 'module.exports.replaceSection = (node) => {', ' if (node.value) {', " node.value = node.value.replace(/green/g, 'blue').replace(/Red/g, 'Blue');", ' }', ' return node;', '};', '```', ].join('\n'), { globalReplaceFunction: node => { if (node.value) { // eslint-disable-next-line no-param-reassign node.value = node.value.replace(/red/g, 'green'); } return node; }, }, ); expect(result).to.equal( ['

Red

', '

More Blue

', '

the sun can get blue

', ''].join('\n'), ); }); it('throws if an import file does not exist', async () => { await expectThrowsAsync(() => execute("```js ::import('./fixtures/not-available.md')\n```"), { errorMatch: /The import "\.\/fixtures\/not-available.md" in "test-file.md" does not exist\. Resolved to ".*"\.$/, }); }); it('throws if an start selector can not be found', async () => { const input = "```js ::import('./fixtures/three-sections-red.md', 'heading:has([value=Does not exit])')\n```"; await expectThrowsAsync(() => execute(input), { errorMatch: /The start selector "heading:has\(\[value=Does not exit\]\)" could not find a matching node in ".*"\.$/, }); }); it('throws if an end selector can not be found', async () => { const input = "```js ::import('./fixtures/three-sections-red.md', 'heading:has([value=More Red])', 'heading:has([value=Does not exit])')\n```"; await expectThrowsAsync(() => execute(input), { errorMatch: /The end selector "heading:has\(\[value=Does not exit\]\)" could not find a matching node in ".*"\./, }); }); it('can import a block (from start headline to next headline same level)', async () => { const result = await execute( [ // '### Static Headline', "```js ::importBlock('./fixtures/three-sections-red.md', '## More Red')", '```', ].join('\n'), ); expect(result).to.equal( [ // '

Static Headline

', '

More Red

', '

the sun can get red

', '', ].join('\n'), ); }); it('can import the last block (from start headline to end of file)', async () => { const result = await execute( [ // '### Static Headline', "```js ::importBlock('./fixtures/three-sections-red.md', '## Additional Red')", '```', ].join('\n'), ); expect(result).to.equal( [ // '

Static Headline

', '

Additional Red

', '

the red sea

', '', ].join('\n'), ); }); it('can import a block content (from start headline (excluding) to next headline same level)', async () => { const result = await execute( [ // '### Static Headline', "```js ::importBlockContent('./fixtures/three-sections-red.md', '## More Red')", '```', ].join('\n'), ); expect(result).to.equal( [ // '

Static Headline

', '

the sun can get red

', '', ].join('\n'), ); }); it('can import a small block (from start headline to next headline of any level)', async () => { const result = await execute( [ // '### Static Headline', "```js ::importSmallBlock('./fixtures/three-sections-red.md', '# Red')", '```', ].join('\n'), ); expect(result).to.equal( [ // '

Static Headline

', '

Red

', '

red is the fire

', '', ].join('\n'), ); }); it('can import a small block content (from start headline (excluding) to next headline of any level)', async () => { const result = await execute( [ // '### Static Headline', "```js ::importSmallBlockContent('./fixtures/three-sections-red.md', '# Red')", '```', ].join('\n'), ); expect(result).to.equal( [ // '

Static Headline

', '

red is the fire

', '', ].join('\n'), ); }); it('resolves imports via node resolution', async () => { const result = await execute( [ // '### Static Headline', "```js ::importBlock('remark-extend/docs/test.md', '## Test')", '```', ].join('\n'), ); expect(result).to.equal( [ '

Static Headline

', '

Test

', '

some image

', '', ].join('\n'), ); }); it('throws if importBlock is used not with headlines', async () => { const input = "```js ::importBlock('./fixtures/three-sections-red.md', 'heading:has([value=More Red])')\n```"; await expectThrowsAsync(() => execute(input), { errorMessage: '::importBlock only works for headlines like "## My Headline" but "heading:has([value=More Red])" was given', }); }); it('can import multiple files', async () => { const result = await execute( [ // '### Static Headline', "```js ::importBlock('./fixtures/three-sections-red.md', '## More Red')", '```', '### Another Static Headline', "```js ::import('./fixtures/import-me.md')", '```', ].join('\n'), ); expect(result).to.equal( [ // '

Static Headline

', '

More Red

', '

the sun can get red

', '

Another Static Headline

', '

import me headline

', '', ].join('\n'), ); }); it('can import files with a table', async () => { const result = await execute( [ // '### Static Headline', "```js ::importBlock('./fixtures/import-table.md', '## Currencies')", '```', ].join('\n'), ); expect(result).to.equal( [ // '

Static Headline

', '

Currencies

', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '
SignName
$Dollar
Euro
', '', ].join('\n'), ); }); });