diff --git a/Makefile b/Makefile deleted file mode 100644 index 05146a4..0000000 --- a/Makefile +++ /dev/null @@ -1,5 +0,0 @@ -TESTS = test/*.js -test: - mocha --timeout 5000 --check-leaks --reporter spec $(TESTS) - -.PHONY: test diff --git a/README.md b/README.md index 71c4ebb..3c1e45f 100755 --- a/README.md +++ b/README.md @@ -1,17 +1,22 @@ # jira2md ## JIRA to MarkDown text format converter -Convert from JIRA text formatting to GitHub Flavored MarkDown and back again. Also allows for both to be converted to HTML. + +Convert from JIRA text formatting to GitHub Flavored Markdown and back again. Also allows for both to be converted to HTML. ## Credits -This module was heavily inspired by the J2M project by Fokke Zandbergen (http://j2m.fokkezb.nl/). Major credit to Fokke (and other contributors) for establishing a lot of the fundamental RexExp patterns for this module to work. + +This module was heavily inspired by the [J2M project by Fokke Zandbergen](http://j2m.fokkezb.nl/). +Major credit to Fokke (and other contributors) for establishing a lot of the fundamental RexExp patterns for this module to work. ## Installation -``` + +```sh npm install jira2md ``` ## Supported Conversions + NOTE: All conversion work bi-directionally (from jira to markdown and back again). * Headers (H1-H6) @@ -34,14 +39,13 @@ NOTE: All conversion work bi-directionally (from jira to markdown and back again * Tables (thanks to erykwarren) * Panels (thanks to erykwarren) - ## How to Use ### Markdown String We'll refer to this as the `md` variable in the examples below. -``` +```md **Some bold things** *Some italic stuff* ## H2 @@ -52,7 +56,7 @@ We'll refer to this as the `md` variable in the examples below. We'll refer to this as the `jira` variable in the examples below. -``` +```jira *Some bold things** _Some italic stuff_ h2. H2 @@ -63,9 +67,9 @@ h2. H2 ```javascript // Include the module -var j2m = require('jira2md'); +var j2m = require('jira2md'); or import jira2md from 'jira2md'; -// If converting from Mardown to Jira Wiki Syntax: +// If converting from Markdown to Jira Wiki Syntax: var jira = j2m.to_jira(md); // If converting from Jira Wiki Syntax to Markdown: @@ -77,3 +81,13 @@ var html = j2m.md_to_html(md); // If converting from JIRA Wiki Syntax to HTML: var html = j2m.jira_to_html(jira); ``` + +### Running tests + +You can run `yarn test` or `npm test` + +### FAQ + +#### Q. Why doesn't this module support conversion of inline markdown? + +A. Jira doesn't support inline code formatting, so the best we can do is to keep the backticks in place. diff --git a/index.js b/index.js index c4e44ec..02bc629 100755 --- a/index.js +++ b/index.js @@ -1,148 +1,217 @@ -var marked = require('marked'); +const marked = require('marked'); marked.setOptions({ - breaks: true, - smartyPants: true + breaks: true, + smartyPants: true }); -var J2M = function() {}; +class J2M { + constructor(str) { + this.str = str; + } -J2M.prototype.md_to_html = function(str) { - return marked(str); + md_to_html(str) { + return marked(str); + }; + + jira_to_html(str) { + return marked(this.to_markdown(str)); + }; + + to_jira(str) { + let hash = splitOutCodeblocks(str, 'toJira'); + return transformHash(hash, 'toJira') + }; + + to_markdown(str) { + let hash = splitOutCodeblocks(str, 'toMarkdown'); + return transformHash(hash, 'toMarkdown') + }; }; -J2M.prototype.jira_to_html = function(str) { - return marked(this.to_markdown(str)); +const transformHash = function (hash, direction) { + let string = '' + + if (direction == 'toMarkdown') { + Object.keys(hash).forEach((key) => { + if (hash[key]['code']) { + string += codeblockToMarkdown(hash[key]['string']); + } else { + string += toMarkdownFormatting(hash[key]['string']); + }; + }); + } else if (direction == 'toJira'){ + Object.keys(hash).forEach((key) => { + if (hash[key]['code']) { + string += codeblockToJira(hash[key]['string']); + } else { + string += toJiraFormatting(hash[key]['string']); + }; + }); + } else { + throw 'Direction is invalid.' + } + return string }; -J2M.prototype.to_markdown = function(str) { - return str - // Ordered Lists - .replace(/^[ \t]*(\*+)\s+/gm, function(match, stars) { - return Array(stars.length).join(" ") + '* '; - }) - // Un-ordered lists - .replace(/^[ \t]*(#+)\s+/gm, function(match, nums) { - return Array(nums.length).join(" ") + '1. '; - }) - // Headers 1-6 - .replace(/^h([0-6])\.(.*)$/gm, function (match, level, content) { - return Array(parseInt(level) + 1).join('#') + content; - }) - // Bold - .replace(/\*(\S.*)\*/g, '**$1**') - // Italic - .replace(/\_(\S.*)\_/g, '*$1*') - // Monospaced text - .replace(/\{\{([^}]+)\}\}/g, '`$1`') - // Citations (buggy) - //.replace(/\?\?((?:.[^?]|[^?].)+)\?\?/g, '$1') - // Inserts - .replace(/\+([^+]*)\+/g, '$1') - // Superscript - .replace(/\^([^^]*)\^/g, '$1') - // Subscript - .replace(/~([^~]*)~/g, '$1') - // Strikethrough - .replace(/\s+-(\S+.*?\S)-\s+/g, ' ~~$1~~ ') - // Code Block - .replace(/\{code(:([a-z]+))?([:|]?(title|borderStyle|borderColor|borderWidth|bgColor|titleBGColor)=.+?)*\}([^]*)\{code\}/gm, '```$2$5```') - // Pre-formatted text - .replace(/{noformat}/g, '```') - // Un-named Links - .replace(/\[([^|]+)\]/g, '<$1>') - // Named Links - .replace(/\[(.+?)\|(.+)\]/g, '[$1]($2)') - // Single Paragraph Blockquote - .replace(/^bq\.\s+/gm, '> ') - // Remove color: unsupported in md - .replace(/\{color:[^}]+\}([^]*)\{color\}/gm, '$1') - // panel into table - .replace(/\{panel:title=([^}]*)\}\n?([^]*?)\n?\{panel\}/gm, '\n| $1 |\n| --- |\n| $2 |') - // table header - .replace(/^[ \t]*((?:\|\|.*?)+\|\|)[ \t]*$/gm, function (match, headers) { - var singleBarred = headers.replace(/\|\|/g,'|'); - return '\n' + singleBarred + '\n' + singleBarred.replace(/\|[^|]+/g, '| --- '); - }) - // remove leading-space of table headers and rows - .replace(/^[ \t]*\|/gm, '|'); +const splitOutCodeblocks = function (str, direction) { + let hash = {}; + let array = []; + // This block returns an array where each element is either a codeblock or is not + if (direction == 'toMarkdown') { + array = str.split(/(\{code[^]*?\{code\}|\{noformat[^]*?\{noformat\})/) + } else if (direction == 'toJira') { + array = str.split(/(```[^]*?```)/) + } else { + return [str] + } + + array.map((string, index) => { + hash[index] = { + string: string, + code: string.includes('```') || string.includes('{code}') || string.includes('{noformat}') + } + }); + + return hash; }; -J2M.prototype.to_jira = function(str) { - var map = { - //cite: '??', - del: '-', - ins: '+', - sup: '^', - sub: '~' - }; - - return str - // Bold, Italic, and Combined (bold+italic) - .replace(/([*_]+)(\S.*?)\1/g, function (match,wrapper,content) { - switch (wrapper.length) { - case 1: return '_' + content + '_'; - case 2: return '*' + content + '*'; - case 3: return '_*' + content + '*_'; - default: return wrapper + content * wrapper; - } - }) - // All Headers (# format) - .replace(/^([#]+)(.*?)$/gm, function (match,level,content) { - return 'h' + level.length + '.' + content; - }) - // Headers (H1 and H2 underlines) - .replace(/^(.*?)\n([=-]+)$/gm, function (match,content,level) { - return 'h' + (level[0] === '=' ? 1 : 2) + '. ' + content; - }) - // Ordered lists - .replace(/^([ \t]*)\d+\.\s+/gm, function(match, spaces) { - return Array(Math.floor(spaces.length/2 + 1)).join("#") + '# '; - }) - // Un-Ordered Lists - .replace(/^([ \t]*)\*\s+/gm, function(match, spaces) { - return Array(Math.floor(spaces.length/2 + 1)).join("*") + '* '; - }) - // Headers (h1 or h2) (lines "underlined" by ---- or =====) - // Citations, Inserts, Subscripts, Superscripts, and Strikethroughs - .replace(new RegExp('<(' + Object.keys(map).join('|') + ')>(.*?)<\/\\1>', 'g'), function (match,from,content) { - var to = map[from]; - return to + content + to; - }) - // Other kind of strikethrough - .replace(/\s+~~(.*?)~~\s+/g, ' -$1- ') - // Named/Un-Named Code Block - .replace(/`{3,}(\w+)?((?:\n|[^`])+)`{3,}/g, function(match, synt, content) { - var code = '{code'; - if (synt) code += ':' + synt; - return code + '}' + content + '{code}'; - }) - // Inline-Preformatted Text - .replace(/`([^`]+)`/g, '{{$1}}') - // Named Link - .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '[$1|$2]') - // Un-Named Link - .replace(/<([^>]+)>/g, '[$1]') - // Single Paragraph Blockquote - .replace(/^>/gm, 'bq.') - // tables - .replace(/^\n((?:\|.*?)+\|)[ \t]*\n((?:\|\s*?\-{3,}\s*?)+\|)[ \t]*\n((?:(?:\|.*?)+\|[ \t]*\n)*)$/gm, - function (match, headerLine, separatorLine, rowstr) { - var headers = headerLine.match(/[^|]+(?=\|)/g); - var separators = separatorLine.match(/[^|]+(?=\|)/g); - if (headers.length !== separators.length) { - return match; - } - var rows = rowstr.split('\n'); - if (rows.length === 1 + 1 && headers.length === 1) { - // panel - return '{panel:title=' + headers[0].trim() + '}\n' + - rowstr.replace(/^\|(.*)[ \t]*\|/, '$1').trim() + - '\n{panel}\n'; - } else { - return '||' + headers.join('||') + '||\n' + rowstr; - } - }); +const codeblockToMarkdown = function (str) { + return str + .replace( + /\{code(:([a-z]+))?([:|]?(title|borderStyle|borderColor|borderWidth|bgColor|titleBGColor)=.+?)*\}([^]*?)\{code\}/gm, '```$2$5```' + ) + // Pre-formatted text + .replace(/{noformat}/g, '```') +}; + +const codeblockToJira = function (str) { + return str + .replace(/`{3,}(\w+)?((?:\n|[^`])+)`{3,}/g, function (_match, synt, content) { + let code = '{code'; + if (synt) code += ':' + synt; + return code + '}' + content + '{code}'; + }) +}; + +const toMarkdownFormatting = function (str) { + return str + // Ordered Lists + .replace(/^[ \t]*(\*+)\s+/gm, function (_match, stars) { + return Array(stars.length).join(" ") + '* '; + }) + // Un-ordered lists + .replace(/^[ \t]*(#+)\s+/gm, function (_match, nums) { + return Array(nums.length).join(" ") + '1. '; + }) + // Headers 1-6 + .replace(/^h([0-6])\.(.*)$/gm, function (_match, level, content) { + return Array(parseInt(level) + 1).join('#') + content; + }) + // Bold + .replace(/(\s|^|\_)\*(\S.*?)\*($|[~`!@#$%^&*(){}\[\];:"'<,.>?\/\\|_+=-]|\s)/g, '$1**$2**$3') + // Italic + .replace(/(\s|^|\*)\_(\S.*?)\_($|[~`!@#$%^&*(){}\[\];:"'<,.>?\/\\|_+=-]|\s)/g, '$1*$2*$3') + // Monospaced text + .replace(/\{\{([^}]+)\}\}/g, '`$1`') + // Citations (buggy) + //.replace(/\?\?((?:.[^?]|[^?].)+)\?\?/g, '$1') + // Inserts + .replace(/\+([^+]*)\+/g, '$1') + // Superscript + .replace(/\^([^^]*)\^/g, '$1') + // Subscript + .replace(/~([^~]*)~/g, '$1') + // Strikethrough + .replace(/(\s|^)+-(\S+.*?\S)-+/g, '$1~~$2~~') + // Un-named Links + .replace(/\[([^|]+)\]/g, '<$1>') + // Named Links + .replace(/\[(.+?)\|(.+)\]/g, '[$1]($2)') + // Single Paragraph Blockquote + .replace(/^bq\.\s+/gm, '> ') + // Remove color: unsupported in md + .replace(/\{color:[^}]+\}([^]*)\{color\}/gm, '$1') + // panel into table + .replace(/\{panel:title=([^}]*)\}\n?([^]*?)\n?\{panel\}/gm, '\n| $1 |\n| --- |\n| $2 |') + // table header + .replace(/^[ \t]*((?:\|\|.*?)+\|\|)[ \t]*$/gm, function (_match, headers) { + const singleBarred = headers.replace(/\|\|/g, '|'); + return '\n' + singleBarred + '\n' + singleBarred.replace(/\|[^|]+/g, '| --- '); + }) + // remove leading-space of table headers and rows + .replace(/^[ \t]*\|/gm, '|'); +}; + +const toJiraFormatting = function (str) { + const map = { + //cite: '??', + del: '-', + ins: '+', + sup: '^', + sub: '~' + }; + + return str + // Bold, Italic, and Combined (bold+italic) + .replace(/(\s?|^)([*_]+)(\S.*?)\2(\s|[~`!@#$%^&()\{\}\[\];:"'<,\.>?\/\\|+=-]|$)/gm, function (_match, opening_chars, wrapper, content, closing_chars) { + switch (wrapper.length) { + case 1: return opening_chars + '_' + content + '_' + closing_chars; + case 2: return opening_chars + "*" + content + "*" + closing_chars; + case 3: return opening_chars + "_*" + content + "*_" + closing_chars; + default: return opening_chars + wrapper + content * wrapper + closing_chars; + } + }) + // All Headers (# format) + .replace(/^([#]+)(.*?)$/gm, function (_match, level, content) { + return 'h' + level.length + '.' + content; + }) + // Headers (H1 and H2 underlines) + .replace(/^(.*?)\n([=-]+)$/gm, function (_match, content, level) { + return 'h' + (level[0] === '=' ? 1 : 2) + '. ' + content; + }) + // Ordered lists + .replace(/^([ \t]*)\d+\.\s+/gm, function (_match, spaces) { + return Array(Math.floor(spaces.length / 2 + 1)).join("#") + '# '; + }) + // Un-Ordered Lists + .replace(/^([ \t]*)\*\s+/gm, function (_match, spaces) { + return Array(Math.floor(spaces.length / 2 + 1)).join("*") + '* '; + }) + // Headers (h1 or h2) (lines "underlined" by ---- or =====) + // Citations, Inserts, Subscripts, Superscripts, and Strikethroughs + .replace(new RegExp('<(' + Object.keys(map).join('|') + ')>(.*?)<\/\\1>', 'g'), function (_match, from, content) { + const to = map[from]; + return to + content + to; + }) + // Other kind of strikethrough + .replace(/(\s|^)+\~~(.*?)\~~+/g, '$1-$2-') + // Inline-Preformatted Text + .replace(/`([^`]+)`/g, '{{$1}}') + // Named Link + .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '[$1|$2]') + // Un-Named Link + .replace(/<([^>]+)>/g, '[$1]') + // Single Paragraph Blockquote + .replace(/^>/gm, 'bq.') + // tables + .replace(/^\n((?:\|.*?)+\|)[ \t]*\n((?:\|\s*?\-{3,}\s*?)+\|)[ \t]*\n((?:(?:\|.*?)+\|[ \t]*\n)*)$/gm, + function (match, headerLine, separatorLine, rowstr) { + const headers = headerLine.match(/[^|]+(?=\|)/g); + const separators = separatorLine.match(/[^|]+(?=\|)/g); + if (headers.length !== separators.length) { + return match; + } + const rows = rowstr.split('\n'); + if (rows.length === 1 + 1 && headers.length === 1) { + // panel + return '{panel:title=' + headers[0].trim() + '}\n' + + rowstr.replace(/^\|(.*)[ \t]*\|/, '$1').trim() + + '\n{panel}\n'; + } else { + return '||' + headers.join('||') + '||\n' + rowstr; + } + }); }; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..5b52021 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,217 @@ +{ + "name": "jira2md", + "version": "2.0.3", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "1.0.0", + "concat-map": "0.0.1" + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" + }, + "chai": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-3.5.0.tgz", + "integrity": "sha1-TQJjewZ/6Vi9v906QOxW/vc3Mkc=", + "dev": true, + "requires": { + "assertion-error": "1.1.0", + "deep-eql": "0.1.3", + "type-detect": "1.0.0" + } + }, + "commander": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", + "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "deep-eql": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-0.1.3.tgz", + "integrity": "sha1-71WKyrjeJSBs1xOQbXTlaTDrafI=", + "dev": true, + "requires": { + "type-detect": "0.1.1" + }, + "dependencies": { + "type-detect": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-0.1.1.tgz", + "integrity": "sha1-C6XsKohWQORw6k6FBZcZANrFiCI=", + "dev": true + } + } + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "glob": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", + "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "requires": { + "fs.realpath": "1.0.0", + "inflight": "1.0.6", + "inherits": "2.0.3", + "minimatch": "3.0.4", + "once": "1.4.0", + "path-is-absolute": "1.0.1" + } + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=" + }, + "he": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", + "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "1.4.0", + "wrappy": "1.0.2" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "marked": { + "version": "0.3.19", + "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.19.tgz", + "integrity": "sha512-ea2eGWOqNxPcXv8dyERdSr/6FmzvWwzjMxpfGB/sbMccXoct+xY+YukPD+QTUZwyvK7BZwcr4m21WBOW41pAkg==" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "1.1.11" + } + }, + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "requires": { + "minimist": "0.0.8" + } + }, + "mocha": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", + "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", + "requires": { + "browser-stdout": "1.3.1", + "commander": "2.15.1", + "debug": "3.1.0", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "glob": "7.1.2", + "growl": "1.10.5", + "he": "1.1.1", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "supports-color": "5.4.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1.0.2" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "supports-color": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", + "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", + "requires": { + "has-flag": "3.0.0" + } + }, + "type-detect": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-1.0.0.tgz", + "integrity": "sha1-diIXzAbbJY7EiQihKY6LlRIejqI=", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + } + } +} diff --git a/package.json b/package.json index c5671cd..c5086a8 100644 --- a/package.json +++ b/package.json @@ -15,15 +15,17 @@ "description": "JIRA to MarkDown text format converter.", "main": "index.js", "devDependencies": { - "chai": "^3.3.0" + "chai": "^3.5.0", + "mocha": "^5.2.0" }, "scripts": { - "test": "make test" + "test": "mocha --timeout 5000 --check-leaks --reporter spec test/*.js" }, "author": "Fokke Zandbergen ", "contributors": [ "Kyle Farris ", - "Eryk Warren " + "Eryk Warren ", + "Kari Matthews " ], "license": "Apache-2.0", "dependencies": { diff --git a/test/jira2md.js b/test/jira2md.js index d518db9..329af8f 100755 --- a/test/jira2md.js +++ b/test/jira2md.js @@ -1,128 +1,226 @@ var should = require('chai').should(); -var expect = require('chai').expect; var fs = require('fs'); var path = require('path'); var j2m = require('../index.js'); -describe('to_markdown', function() { - it('should exist', function() { - should.exist(j2m.to_markdown); - }); - it('should be a function', function() { - j2m.to_markdown.should.be.a('function'); - }); - it('should convert bolds properly', function() { - var markdown = j2m.to_markdown('*bold*'); - markdown.should.eql('**bold**'); +describe('to_markdown', function () { + it('should exist', function () { + should.exist(j2m.to_markdown); + }); + + it('should be a function', function () { + j2m.to_markdown.should.be.a('function'); + }); + + describe('emphasis formatting', function () { + describe('bold formatting', function () { + it('should convert bolds properly', function () { + var markdown = j2m.to_markdown('*bold words*'); + markdown.should.eql('**bold words**'); + }); + + it("should handle multiple bold sections in a line", function () { + var markdown = j2m.to_markdown("*this should be bold* this should not *this should be bold*"); + markdown.should.eql("**this should be bold** this should not **this should be bold**"); + }); + + it("does not perform intraword formatting on asterisks", function () { + var markdown = j2m.to_markdown("a*phrase*with*asterisks"); + markdown.should.eql("a*phrase*with*asterisks"); + }); + + it('does not apply bold formatting without an underscore at the end of the phrase', function () { + var markdown = j2m.to_markdown('*a*phrase'); + markdown.should.eql('*a*phrase'); + }); + + it('formats bolds while leaving intraword asterisks untouched', function () { + var markdown = j2m.to_markdown('*bold*phrase*with*internal*asterisks*'); + markdown.should.eql('**bold*phrase*with*internal*asterisks**'); + }); + + it('handles bolds at the end of sentences', function () { + var markdown = j2m.to_markdown('A sentence ending in *bold*.'); + markdown.should.eql('A sentence ending in **bold**.'); + }); }); - it('should convert italics properly', function() { + + describe('italic formatting', function () { + it('should convert italics properly', function () { var markdown = j2m.to_markdown('_italic_'); markdown.should.eql('*italic*'); + }); + + it("should handle multiple italic sections in a line", function () { + var markdown = j2m.to_markdown("_this should be italic_ this should not _this should be italic_"); + markdown.should.eql("*this should be italic* this should not *this should be italic*"); + }); + + it('does not perform intraword formatting on underscores', function () { + var markdown = j2m.to_markdown('a_phrase_with_underscores'); + markdown.should.eql('a_phrase_with_underscores'); + }); + + it('does not apply italic formatting without an underscore at the end of the phrase', function () { + var markdown = j2m.to_markdown('_a_phrase'); + markdown.should.eql('_a_phrase'); + }); + + it('formats italics while leaving intraword underscores untouched', function () { + var markdown = j2m.to_markdown('_italic_phrase_with_internal_underscores_'); + markdown.should.eql('*italic_phrase_with_internal_underscores*'); + }); + + it('handles italics at the end of sentences', function () { + var markdown = j2m.to_markdown('A sentence ending in _italic_.'); + markdown.should.eql('A sentence ending in *italic*.'); + }); }); - it('should convert monospaced content properly', function() { - var markdown = j2m.to_markdown('{{monospaced}}'); - markdown.should.eql('`monospaced`'); - }); - //it('should convert citations properly', function() { - // var markdown = j2m.to_markdown('??citation??'); - // markdown.should.eql('citation'); - //}); - it('should convert strikethroughs properly', function() { - var markdown = j2m.to_markdown('-deleted-'); - markdown.should.eql('~~deleted~~'); - }); - it('should convert inserts properly', function() { - var markdown = j2m.to_markdown('+inserted+'); - markdown.should.eql('inserted'); - }); - it('should convert superscript properly', function() { - var markdown = j2m.to_markdown('^superscript^'); - markdown.should.eql('superscript'); - }); - it('should convert subscript properly', function() { - var markdown = j2m.to_markdown('~subscript~'); - markdown.should.eql('subscript'); - }); - it('should convert preformatted blocks properly', function() { - var markdown = j2m.to_markdown("{noformat}\nso *no* further _formatting_ is done here\n{noformat}"); - markdown.should.eql("```\nso **no** further *formatting* is done here\n```"); - }); - it('should convert language-specific code blocks properly', function() { - var markdown = j2m.to_markdown("{code:javascript}\nvar hello = 'world';\n{code}"); - markdown.should.eql("```javascript\nvar hello = 'world';\n```"); - }); - it('should convert code without language-specific and with title into code block', function() { - var markdown = j2m.to_markdown("{code:title=Foo.java}\nclass Foo {\n public static void main() {\n }\n}\n{code}"); - markdown.should.eql("```\nclass Foo {\n public static void main() {\n }\n}\n```") - }); - it('should convert fully configured code block', function() { - var markdown = j2m.to_markdown( - "{code:xml|title=My Title|borderStyle=dashed|borderColor=#ccc|titleBGColor=#F7D6C1|bgColor=#FFFFCE}" - + "\n " - + "\n " - + "\n " - + "\n{code}"); - markdown.should.eql( - "```xml" - + "\n " - + "\n " - + "\n " - + "\n```"); - }); - it('should convert unnamed links properly', function() { - var markdown = j2m.to_markdown("[http://google.com]"); - markdown.should.eql(""); - }); - it('should convert named links properly', function() { - var markdown = j2m.to_markdown("[Google|http://google.com]"); - markdown.should.eql("[Google](http://google.com)"); - }); - it('should convert headers properly', function() { - var h1 = j2m.to_markdown("h1. Biggest heading"); - var h2 = j2m.to_markdown("h2. Bigger heading"); - var h3 = j2m.to_markdown("h3. Big heading"); - var h4 = j2m.to_markdown("h4. Normal heading"); - var h5 = j2m.to_markdown("h5. Small heading"); - var h6 = j2m.to_markdown("h6. Smallest heading"); - h1.should.eql("# Biggest heading"); - h2.should.eql("## Bigger heading"); - h3.should.eql("### Big heading"); - h4.should.eql("#### Normal heading"); - h5.should.eql("##### Small heading"); - h6.should.eql("###### Smallest heading"); + + it('should handle bold AND italic (combined) correctly', function () { + var markdown = j2m.to_markdown("This is _*emphatically bold*_!"); + markdown.should.eql("This is ***emphatically bold***!"); }); - it('should convert blockquotes properly', function() { - var markdown = j2m.to_markdown("bq. This is a long blockquote type thingy that needs to be converted."); - markdown.should.eql("> This is a long blockquote type thingy that needs to be converted."); + }); + + it('should convert monospaced content properly', function () { + var markdown = j2m.to_markdown('{{monospaced}}'); + markdown.should.eql('`monospaced`'); + }); + + //it('should convert citations properly', function() { + // var markdown = j2m.to_markdown('??citation??'); + // markdown.should.eql('citation'); + //}); + + it('should convert strikethroughs properly', function () { + var markdown = j2m.to_markdown('-deleted-'); + markdown.should.eql('~~deleted~~'); + }); + + it('should convert inserts properly', function () { + var markdown = j2m.to_markdown('+inserted+'); + markdown.should.eql('inserted'); + }); + + it('should convert superscript properly', function () { + var markdown = j2m.to_markdown('^superscript^'); + markdown.should.eql('superscript'); + }); + + it('should convert subscript properly', function () { + var markdown = j2m.to_markdown('~subscript~'); + markdown.should.eql('subscript'); + }); + + it('should convert preformatted blocks properly', function () { + var markdown = j2m.to_markdown("{noformat}\nso *no* further _formatting_ is done here\n{noformat}"); + markdown.should.eql("```\nso *no* further _formatting_ is done here\n```"); + }); + + describe('code block formatting', function () { + it('should convert language-specific code blocks properly', function () { + var markdown = j2m.to_markdown("{code:javascript}\nvar hello = 'world';\n{code}"); + markdown.should.eql("```javascript\nvar hello = 'world';\n```"); }); - it('should convert un-ordered lists properly', function() { - var markdown = j2m.to_markdown("* Foo\n* Bar\n* Baz\n** FooBar\n** BarBaz\n*** FooBarBaz\n* Starting Over"); - markdown.should.eql("* Foo\n* Bar\n* Baz\n * FooBar\n * BarBaz\n * FooBarBaz\n* Starting Over"); + + it('should convert code without language-specific and with title into code block', function () { + var markdown = j2m.to_markdown("{code:title=Foo.java}\nclass Foo {\n public static void main() {\n }\n}\n{code}"); + markdown.should.eql("```\nclass Foo {\n public static void main() {\n }\n}\n```"); }); - it('should convert ordered lists properly', function() { - var markdown = j2m.to_markdown("# Foo\n# Bar\n# Baz\n## FooBar\n## BarBaz\n### FooBarBaz\n# Starting Over"); - markdown.should.eql("1. Foo\n1. Bar\n1. Baz\n 1. FooBar\n 1. BarBaz\n 1. FooBarBaz\n1. Starting Over"); + + it('should convert fully configured code block', function () { + var markdown = j2m.to_markdown( + "{code:xml|title=My Title|borderStyle=dashed|borderColor=#ccc|titleBGColor=#F7D6C1|bgColor=#FFFFCE}" + + "\n " + + "\n " + + "\n " + + "\n{code}"); + markdown.should.eql( + "```xml" + + "\n " + + "\n " + + "\n " + + "\n```"); }); - it('should handle bold AND italic (combined) correctly', function() { - var markdown = j2m.to_markdown("This is _*emphatically bold*_!"); - markdown.should.eql("This is ***emphatically bold***!"); + + it('should convert multiple codeblocks properly', function () { + var markdown = j2m.to_markdown("{code:title=Foo.java}\nclass Foo {\n public static void main() {\n }\n}\n{code} \n {code:title=Foo.java}\nclass Foo {\n public static void main() {\n }\n}\n{code}"); + markdown.should.eql("```\nclass Foo {\n public static void main() {\n }\n}\n``` \n ```\nclass Foo {\n public static void main() {\n }\n}\n```"); }); - it('should handle bold within a un-ordered list item', function() { - var markdown = j2m.to_markdown("* This is not bold!\n** This is *bold*."); - markdown.should.eql("* This is not bold!\n * This is **bold**."); + + it('should not apply formatting within codeblocks', function () { + var markdown = j2m.to_markdown("{code}\nso *no* further _formatting_ is done here\n{code}"); + markdown.should.eql("```\nso *no* further _formatting_ is done here\n```"); }); - it('should be able to handle a complicated multi-line jira-wiki string and convert it to markdown', function() { - var jira_str = fs.readFileSync(path.resolve(__dirname, 'test.jira'),"utf8"); - var md_str = fs.readFileSync(path.resolve(__dirname, 'test.md'),"utf8"); - var markdown = j2m.to_markdown(jira_str); - markdown.should.eql(md_str); + }); + + it('should convert unnamed links properly', function () { + var markdown = j2m.to_markdown("[http://google.com]"); + markdown.should.eql(""); + }); + + it('should convert named links properly', function () { + var markdown = j2m.to_markdown("[Google|http://google.com]"); + markdown.should.eql("[Google](http://google.com)"); + }); + + it('should convert headers properly', function () { + var h1 = j2m.to_markdown("h1. Biggest heading"); + var h2 = j2m.to_markdown("h2. Bigger heading"); + var h3 = j2m.to_markdown("h3. Big heading"); + var h4 = j2m.to_markdown("h4. Normal heading"); + var h5 = j2m.to_markdown("h5. Small heading"); + var h6 = j2m.to_markdown("h6. Smallest heading"); + h1.should.eql("# Biggest heading"); + h2.should.eql("## Bigger heading"); + h3.should.eql("### Big heading"); + h4.should.eql("#### Normal heading"); + h5.should.eql("##### Small heading"); + h6.should.eql("###### Smallest heading"); + }); + + it('should convert blockquotes properly', function () { + var markdown = j2m.to_markdown("bq. This is a long blockquote type thingy that needs to be converted."); + markdown.should.eql("> This is a long blockquote type thingy that needs to be converted."); + }); + + describe('list formatting', function () { + it('should convert un-ordered lists properly', function () { + var markdown = j2m.to_markdown("* Foo\n* Bar\n* Baz\n** FooBar\n** BarBaz\n*** FooBarBaz\n* Starting Over"); + markdown.should.eql("* Foo\n* Bar\n* Baz\n * FooBar\n * BarBaz\n * FooBarBaz\n* Starting Over"); }); - it('should not recognize strikethroughs over multiple lines', function() { - var markdown = j2m.to_markdown("* Here's an un-ordered list line\n* Multi-line strikethroughs shouldn't work."); - markdown.should.eql("* Here's an un-ordered list line\n* Multi-line strikethroughs shouldn't work."); + + it('should convert ordered lists properly', function () { + var markdown = j2m.to_markdown("# Foo\n# Bar\n# Baz\n## FooBar\n## BarBaz\n### FooBarBaz\n# Starting Over"); + markdown.should.eql("1. Foo\n1. Bar\n1. Baz\n 1. FooBar\n 1. BarBaz\n 1. FooBarBaz\n1. Starting Over"); }); - it('should remove color attributes', function() { - var markdown = j2m.to_markdown("A text with{color:blue} blue \n lines {color} is not necessary."); - markdown.should.eql("A text with blue \n lines is not necessary."); + + it('should handle bold within a un-ordered list item', function () { + var markdown = j2m.to_markdown("* This is not bold!\n** This is *bold*."); + markdown.should.eql("* This is not bold!\n * This is **bold**."); }); + }); + + it('should be able to handle a complicated multi-line jira-wiki string and convert it to markdown', function () { + var jira_str = fs.readFileSync(path.resolve(__dirname, 'test.jira'), "utf8"); + var md_str = fs.readFileSync(path.resolve(__dirname, 'test.md'), "utf8"); + var markdown = j2m.to_markdown(jira_str); + markdown.should.eql(md_str); + }); + + it('should not recognize strikethroughs over multiple lines', function () { + var markdown = j2m.to_markdown("* Here's an un-ordered list line\n* Multi-line strikethroughs shouldn't work."); + markdown.should.eql("* Here's an un-ordered list line\n* Multi-line strikethroughs shouldn't work."); + }); + + it('should remove color attributes', function () { + var markdown = j2m.to_markdown("A text with{color:blue} blue \n lines {color} is not necessary."); + markdown.should.eql("A text with blue \n lines is not necessary."); + }); + + it('should leave urls unchanged', function () { + var markdown = j2m.to_markdown('https://example_url_thing.com'); + markdown.should.eql('https://example_url_thing.com'); + }); }); diff --git a/test/md2jira.js b/test/md2jira.js index 5774317..d6798c1 100755 --- a/test/md2jira.js +++ b/test/md2jira.js @@ -4,105 +4,209 @@ var fs = require('fs'); var path = require('path'); var j2m = require('../index.js'); -describe('to_jira', function() { - it('should exist', function() { - should.exist(j2m.to_jira); - }); - it('should be a function', function() { - j2m.to_jira.should.be.a('function'); - }); - it('should convert bolds properly', function() { - var jira = j2m.to_jira('**bold**'); - jira.should.eql('*bold*'); - }); - it('should convert italics properly', function() { - var jira = j2m.to_jira('*italic*'); - jira.should.eql('_italic_'); - }); - it('should convert monospaced content properly', function() { - var jira = j2m.to_jira('`monospaced`'); - jira.should.eql('{{monospaced}}'); - }); - it('should convert citations properly', function() { - var jira = j2m.to_jira('citation'); - jira.should.eql('??citation??'); - }); - it('should convert strikethroughs properly', function() { - var jira = j2m.to_jira('~~deleted~~'); - jira.should.eql('-deleted-'); - }); - it('should convert inserts properly', function() { - var jira = j2m.to_jira('inserted'); - jira.should.eql('+inserted+'); - }); - it('should convert superscript properly', function() { - var jira = j2m.to_jira('superscript'); - jira.should.eql('^superscript^'); - }); - it('should convert subscript properly', function() { - var jira = j2m.to_jira('subscript'); - jira.should.eql('~subscript~'); - }); - it('should convert preformatted blocks properly', function() { - var jira = j2m.to_jira("```\nso *no* further **formatting** is done here\n```"); - jira.should.eql("{code}\nso _no_ further *formatting* is done here\n{code}"); - }); - it('should convert language-specific code blocks properly', function() { - var jira = j2m.to_jira("```javascript\nvar hello = 'world';\n```"); - jira.should.eql("{code:javascript}\nvar hello = 'world';\n{code}"); - }); - it('should convert unnamed links properly', function() { - var jira = j2m.to_jira(""); - jira.should.eql("[http://google.com]"); - }); - it('should convert named links properly', function() { - var jira = j2m.to_jira("[Google](http://google.com)"); - jira.should.eql("[Google|http://google.com]"); - }); - it('should convert headers properly', function() { - var h1 = j2m.to_jira("# Biggest heading"); - var h2 = j2m.to_jira("## Bigger heading"); - var h3 = j2m.to_jira("### Big heading"); - var h4 = j2m.to_jira("#### Normal heading"); - var h5 = j2m.to_jira("##### Small heading"); - var h6 = j2m.to_jira("###### Smallest heading"); - h1.should.eql("h1. Biggest heading"); - h2.should.eql("h2. Bigger heading"); - h3.should.eql("h3. Big heading"); - h4.should.eql("h4. Normal heading"); - h5.should.eql("h5. Small heading"); - h6.should.eql("h6. Smallest heading"); +describe('to_jira', function () { + it('should exist', function () { + should.exist(j2m.to_jira); + }); + + it('should be a function', function () { + j2m.to_jira.should.be.a('function'); + }); + + describe('emphasis formatting', function () { + describe('bold formatting', function () { + it('should convert bolds properly', function () { + var jira = j2m.to_jira('**bold words**'); + jira.should.eql('*bold words*'); + }); + + it("should handle multiple bold sections in a line", function () { + var jira = j2m.to_jira("**this should be bold** this should not **this should be bold**"); + jira.should.eql("*this should be bold* this should not *this should be bold*"); + }); + + it("does not perform intraword formatting on asterisks", function () { + var jira = j2m.to_jira("a*phrase*with*asterisks"); + jira.should.eql("a*phrase*with*asterisks"); + }); + + it('handles bolds at the end of sentences', function () { + var jira = j2m.to_jira('A sentence ending in **bold**.'); + jira.should.eql('A sentence ending in *bold*.'); + }); + + it('formats bolds while leaving intraword asterisks untouched', function () { + var jira = j2m.to_jira('**bold*phrase*with*internal*asterisks**'); + jira.should.eql('*bold*phrase*with*internal*asterisks*'); + }); + + // TODO: Fix the code so this test can be unskipped + it.skip('does not apply bold formatting without an asterisk pair at the start of the phrase', function () { + var jira = j2m.to_jira('a**phrase**'); + jira.should.eql('a**phrase**'); + }); + + it('does not apply bold formatting without an asterisk pair at the end of the phrase', function () { + var jira = j2m.to_jira('**a**phrase'); + jira.should.eql('**a**phrase'); + }); + }) + + describe('italic formatting', function () { + it('should convert italics properly', function () { + var jira = j2m.to_jira('*italic words*'); + jira.should.eql('_italic words_'); + }); + + it("does not perform intraword formatting on underscores", function () { + var jira = j2m.to_jira("a_phrase_with_underscores"); + jira.should.eql("a_phrase_with_underscores"); + }); + + it('formats italics while leaving intraword underscores untouched', function () { + var jira = j2m.to_jira('_italic_phrase_with_internal_underscores_'); + jira.should.eql('_italic_phrase_with_internal_underscores_'); + }); + + it('handles italics at the end of sentences', function () { + var jira = j2m.to_jira('A sentence ending in *italic*.'); + jira.should.eql('A sentence ending in _italic_.'); + }); + + // TODO: Fix the code so this test can be unskipped + it.skip('does not apply italic formatting without asterisks at the start of the phrase', function () { + var jira = j2m.to_jira('a*phrase*'); + jira.should.eql('a*phrase*'); + }); + + it('does not apply italic formatting without asterisks at the end of the phrase', function () { + var jira = j2m.to_jira('*a*phrase'); + jira.should.eql('*a*phrase'); + }); + }) + + it('should handle bold AND italic (combined) correctly', function () { + var jira = j2m.to_jira("This is ***emphatically bold***!"); + jira.should.eql("This is _*emphatically bold*_!"); }); - it('should convert underline-style headers properly', function() { - var h1 = j2m.to_jira("Biggest heading\n======="); - var h2 = j2m.to_jira("Bigger heading\n------"); - h1.should.eql("h1. Biggest heading"); - h2.should.eql("h2. Bigger heading"); + + it('handles a bold word followed by an italic word', function () { + var jira = j2m.to_jira('**bold** *italic*'); + jira.should.eql('*bold* _italic_'); }); - it('should convert blockquotes properly', function() { - var jira = j2m.to_jira("> This is a long blockquote type thingy that needs to be converted."); - jira.should.eql("bq. This is a long blockquote type thingy that needs to be converted."); + }); + + it('should convert monospaced content properly', function () { + var jira = j2m.to_jira('`monospaced words`'); + jira.should.eql('{{monospaced words}}'); + }); + + // it('should convert citations properly', function () { + // var jira = j2m.to_jira('citation'); + // jira.should.eql('??citation??'); + // }); + + it('should convert strikethroughs properly', function () { + var jira = j2m.to_jira('~~deleted~~'); + jira.should.eql('-deleted-'); + }); + + it('should convert inserts properly', function () { + var jira = j2m.to_jira('inserted'); + jira.should.eql('+inserted+'); + }); + + it('should convert superscript properly', function () { + var jira = j2m.to_jira('superscript'); + jira.should.eql('^superscript^'); + }); + + it('should convert subscript properly', function () { + var jira = j2m.to_jira('subscript'); + jira.should.eql('~subscript~'); + }); + + describe('codeblock formatting', function () { + it('should convert language-specific code blocks properly', function () { + var jira = j2m.to_jira("```javascript\nvar hello = 'world';\n```"); + jira.should.eql("{code:javascript}\nvar hello = 'world';\n{code}"); }); - it('should convert un-ordered lists properly', function() { - var jira = j2m.to_jira("* Foo\n* Bar\n* Baz\n * FooBar\n * BarBaz\n * FooBarBaz\n* Starting Over"); - jira.should.eql("* Foo\n* Bar\n* Baz\n** FooBar\n** BarBaz\n*** FooBarBaz\n* Starting Over"); + + it('should convert multiple codeblocks properly', function () { + var jira = j2m.to_jira("```javascript\nvar hello = 'world';\n``` \n```javascript\nvar hello = 'world';\n```"); + jira.should.eql("{code:javascript}\nvar hello = 'world';\n{code} \n{code:javascript}\nvar hello = 'world';\n{code}"); }); - it('should convert ordered lists properly', function() { - var jira = j2m.to_jira("1. Foo\n1. Bar\n1. Baz\n 1. FooBar\n 1. BarBaz\n 1. FooBarBaz\n1. Starting Over"); - jira.should.eql("# Foo\n# Bar\n# Baz\n## FooBar\n## BarBaz\n### FooBarBaz\n# Starting Over"); + + it('should not apply formatting within codeblocks', function () { + var jira = j2m.to_jira("```\nso **no** further *formatting* _is_ done ***here***\n```"); + jira.should.eql("{code}\nso **no** further *formatting* _is_ done ***here***\n{code}"); }); - it('should handle bold AND italic (combined) correctly', function() { - var jira = j2m.to_jira("This is ***emphatically bold***!"); - jira.should.eql("This is _*emphatically bold*_!"); + }); + + it('should convert unnamed links properly', function () { + var jira = j2m.to_jira(""); + jira.should.eql("[http://google.com]"); + }); + + it('should convert named links properly', function () { + var jira = j2m.to_jira("[Google](http://google.com)"); + jira.should.eql("[Google|http://google.com]"); + }); + + it('should convert headers properly', function () { + var h1 = j2m.to_jira("# Biggest heading"); + var h2 = j2m.to_jira("## Bigger heading"); + var h3 = j2m.to_jira("### Big heading"); + var h4 = j2m.to_jira("#### Normal heading"); + var h5 = j2m.to_jira("##### Small heading"); + var h6 = j2m.to_jira("###### Smallest heading"); + h1.should.eql("h1. Biggest heading"); + h2.should.eql("h2. Bigger heading"); + h3.should.eql("h3. Big heading"); + h4.should.eql("h4. Normal heading"); + h5.should.eql("h5. Small heading"); + h6.should.eql("h6. Smallest heading"); + }); + + it('should convert underline-style headers properly', function () { + var h1 = j2m.to_jira("Biggest heading\n======="); + var h2 = j2m.to_jira("Bigger heading\n------"); + h1.should.eql("h1. Biggest heading"); + h2.should.eql("h2. Bigger heading"); + }); + + it('should convert blockquotes properly', function () { + var jira = j2m.to_jira("> This is a long blockquote type thingy that needs to be converted."); + jira.should.eql("bq. This is a long blockquote type thingy that needs to be converted."); + }); + + describe('list formatting', function () { + it('should convert un-ordered lists properly', function () { + var jira = j2m.to_jira("* Foo\n* Bar\n* Baz\n * FooBar\n * BarBaz\n * FooBarBaz\n* Starting Over"); + jira.should.eql("* Foo\n* Bar\n* Baz\n** FooBar\n** BarBaz\n*** FooBarBaz\n* Starting Over"); }); - it('should handle bold within a un-ordered list item', function() { - var jira = j2m.to_jira("* This is not bold!\n * This is **bold**."); - jira.should.eql("* This is not bold!\n** This is *bold*."); + + it('should convert ordered lists properly', function () { + var jira = j2m.to_jira("1. Foo\n1. Bar\n1. Baz\n 1. FooBar\n 1. BarBaz\n 1. FooBarBaz\n1. Starting Over"); + jira.should.eql("# Foo\n# Bar\n# Baz\n## FooBar\n## BarBaz\n### FooBarBaz\n# Starting Over"); }); - it('should be able to handle a complicated multi-line markdown string and convert it to markdown', function() { - var jira_str = fs.readFileSync(path.resolve(__dirname, 'test.jira'),"utf8"); - var md_str = fs.readFileSync(path.resolve(__dirname, 'test.md'),"utf8"); - var jira = j2m.to_jira(md_str); - jira.should.eql(jira_str); + + it('should handle bold within a un-ordered list item', function () { + var jira = j2m.to_jira("* This is not bold!\n * This is **bold**."); + jira.should.eql("* This is not bold!\n** This is *bold*."); }); + }); + + it('should be able to handle a complicated multi-line markdown string and convert it to markdown', function () { + var jira_str = fs.readFileSync(path.resolve(__dirname, 'test.jira'), "utf8"); + var md_str = fs.readFileSync(path.resolve(__dirname, 'test.md'), "utf8"); + var jira = j2m.to_jira(md_str); + jira.should.eql(jira_str); + }); + + it('should leave urls and emails unchanged', function () { + var jira = j2m.to_jira('https://example_url_thing.com some_person@example_domain.com'); + jira.should.eql('https://example_url_thing.com some_person@example_domain.com'); + }); }); + diff --git a/test/test.jira b/test/test.jira index ef90b6c..79113ab 100755 --- a/test/test.jira +++ b/test/test.jira @@ -12,7 +12,6 @@ h6. Smallest heading *strong* _emphasis_ {{monospaced}} -??citation?? -deleted- +inserted+ ^superscript^ @@ -48,9 +47,10 @@ _*Should be bold AND italic*_ # Back to first level li * Here's _italic_ inside li -* here's *bold* inside li +* Here's *bold* inside li +* Here's *bold* and _italic_ inside li * Here's _*bold + italic*_ inside li -** Here they are in one line indented: _italic_ *bold* +** Here they are in one line indented: _italic_ and *bold* bq. Here's a long single-paragraph block quote. It should look pretty and stuff. diff --git a/test/test.md b/test/test.md index 701b29f..a9a236e 100755 --- a/test/test.md +++ b/test/test.md @@ -12,7 +12,6 @@ **strong** *emphasis* `monospaced` -citation ~~deleted~~ inserted superscript @@ -20,7 +19,7 @@ ```javascript var hello = 'world'; -{code} +``` [Google](http://google.com) @@ -28,9 +27,9 @@ var hello = 'world'; GitHub Flavor ~~deleted~~ -{code} +``` preformatted piece of text - so *no_ further _formatting* is done here + so _no_ further _formatting_ is done here ``` ***Should be bold AND italic*** @@ -48,9 +47,10 @@ GitHub Flavor 1. Back to first level li * Here's *italic* inside li -* here's **bold** inside li +* Here's **bold** inside li +* Here's **bold** and *italic* inside li * Here's ***bold + italic*** inside li - * Here they are in one line indented: *italic* **bold** + * Here they are in one line indented: *italic* and **bold** > Here's a long single-paragraph block quote. It should look pretty and stuff.