/* eslint-disable max-lines */ const dedent = require('dedent-tabs').default; const Markdown = require('naturalcrit/markdown.js'); // Marked.js adds line returns after closing tags on some default tokens. // This removes those line returns for comparison sake. String.prototype.trimReturns = function(){ return this.replace(/\r?\n|\r/g, '').trim(); }; renderAllPages = function(pages){ const outputs = []; pages.forEach((page, index) => { const output = Markdown.render(page, index); outputs.push(output); }); return outputs; }; // Adding `.failing()` method to `describe` or `it` will make failing tests "pass" as long as they continue to fail. // Remove the `.failing()` method once you have fixed the issue. describe('Block-level variables', ()=>{ it('Handles variable assignment and recall with simple text', function() { const source = dedent` [var]: string $[var] `; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('
string
'); }); it('Handles variable assignment and recall with multiline string', function() { const source = dedent` [var]: string across multiple lines $[var]`; const rendered = Markdown.render(source).replace(/\s/g,' ').trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('string across multiple lines
'); }); it('Handles variable assignment and recall with tables', function() { const source = dedent` [var]: ##### Title | H1 | H2 | |:---|:--:| | A | B | | C | D | $[var]`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`| H1 | H2 |
|---|---|
| A | B |
| C | D |
string
'); }); it('Hoists last instance of variable', function() { const source = dedent` $[var] [var]: string [var]: new string`; const rendered = Markdown.render(source).replace(/\s/g,' ').trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('new string
'); }); it('Handles complex hoisting', function() { const source = dedent` $[titleAndName]: $[title] $[fullName] $[title]: Mr. $[fullName]: $[firstName] $[lastName] [firstName]: Bob Welcome, $[titleAndName]! [lastName]: Jacob [lastName]: $[lastName]son `; const rendered = Markdown.render(source).replace(/\s/g,' ').trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('Welcome, Mr. Bob Jacobson!
'); }); it('Handles variable reassignment', function() { const source = dedent` [var]: one $[var] [var]: two $[var] `; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('one
two
'.trimReturns()); }); it('Handles variable reassignment with hoisting', function() { const source = dedent` $[var] [var]: one $[var] [var]: two $[var] `; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('two
one
two
'.trimReturns()); }); it("Ignores undefined variables that can't be hoisted", function() { const source = dedent` $[var](My name is $[first] $[last]) $[last]: Jones `; const rendered = Markdown.render(source).replace(/\s/g,' ').trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`My name is $[first] Jones
`.trimReturns()); }); }); describe('Inline-level variables', ()=>{ it('Handles variable assignment and recall with simple text', function() { const source = dedent` $[var](string) $[var] `; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('string
string
'); }); it('Hoists undefined variables when possible', function() { const source = dedent` $[var](My name is $[name] Jones) [name]: Bob`; const rendered = Markdown.render(source).replace(/\s/g,' ').trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('My name is Bob Jones
'); }); it('Hoists last instance of variable', function() { const source = dedent` $[var](My name is $[name] Jones) $[name](Bob) [name]: Bill`; const rendered = Markdown.render(source).replace(/\s/g,' ').trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`My name is Bill Jones
Bob
`.trimReturns()); }); it('Only captures nested parens if balanced', function() { const source = dedent` $[var1](A variable (with nested parens) inside) $[var1] $[var2](A variable ) with unbalanced parens) $[var2]`; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`A variable (with nested parens) inside
A variable (with nested parens) inside
A variable with unbalanced parens)
A variable
`.trimReturns()); }); }); describe('Math', ()=>{ it('Handles simple math using numbers only', function() { const source = dedent` $[1 + 3 * 5 - (1 / 4)] `; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('15.75
'); }); it('Handles round function', function() { const source = dedent` $[round(1/4)]`; const rendered = Markdown.render(source).replace(/\s/g,' ').trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('0
'); }); it('Handles floor function', function() { const source = dedent` $[floor(0.6)]`; const rendered = Markdown.render(source).replace(/\s/g,' ').trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('0
'); }); it('Handles ceil function', function() { const source = dedent` $[ceil(0.2)]`; const rendered = Markdown.render(source).replace(/\s/g,' ').trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('1
'); }); it('Handles nested functions', function() { const source = dedent` $[ceil(floor(round(0.6)))]`; const rendered = Markdown.render(source).replace(/\s/g,' ').trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('1
'); }); it('Handles simple math with variables', function() { const source = dedent` $[num1]: 5 $[num2]: 4 Answer is $[answer]($[1 + 3 * num1 - (1 / num2)]). `; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('Answer is 15.75.
'); }); it('Handles variable incrementing', function() { const source = dedent` $[num1]: 5 Increment num1 to get $[num1]($[num1 + 1]) and again to $[num1]($[num1 + 1]). `; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe('Increment num1 to get 6 and again to 7.
'); }); }); describe('Code blocks', ()=>{ it('Ignores all variables in fenced code blocks', function() { const source = dedent` \`\`\` [var]: string $[var] $[var](new string) \`\`\` `; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
[var]: string
$[var]
$[var](new string)
`.trimReturns());
});
it('Ignores all variables in indented code blocks', function() {
const source = dedent`
test
[var]: string
$[var]
$[var](new string)
`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
test
[var]: string
$[var]
$[var](new string)
`.trimReturns());
});
it('Ignores all variables in inline code blocks', function() {
const source = '[var](Hello) `[link](url)`. This `[var] does not work`';
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`
var [link](url). This [var] does not work
An image !
A Link to my website!
`.trimReturns()); }); it('Renders normal links with a title', function() { const source = 'A Link to my [website](url "and title")!'; const rendered = Markdown.render(source).trimReturns(); expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(dedent`A Link to my website!
`.trimReturns()); }); }); describe('Cross-page variables', ()=>{ it('Handles variable assignment and recall across pages', function() { const source0 = `[var]: string`; const source1 = `$[var]`; const rendered = renderAllPages([source0, source1]).join('\n\\page\n').trimReturns(); expect(rendered, `Input:\n${[source0,source1].join('\n\\page\n')}`, { showPrefix: false }).toBe('\\pagestring
'); }); it('Handles hoisting across pages', function() { const source0 = `$[var]`; const source1 = `[var]: string`; renderAllPages([source0, source1]).join('\n\\page\n').trimReturns(); //Requires one full render of document before hoisting is picked up const rendered = renderAllPages([source0, source1]).join('\n\\page\n').trimReturns(); expect(rendered, `Input:\n${[source0,source1].join('\n\\page\n')}`, { showPrefix: false }).toBe('string
\\page'); }); it('Handles reassignment and hoisting across pages', function() { const source0 = `$[var]\n\n[var]: one\n\n$[var]`; const source1 = `[var]: two\n\n$[var]`; renderAllPages([source0, source1]).join('\n\\page\n').trimReturns(); //Requires one full render of document before hoisting is picked up const rendered = renderAllPages([source0, source1]).join('\n\\page\n').trimReturns(); expect(rendered, `Input:\n${[source0,source1].join('\n\\page\n')}`, { showPrefix: false }).toBe('two
one
\\pagetwo
'); }); });