From 44f0bb9109a63cf271dcf46786027f41de7efb22 Mon Sep 17 00:00:00 2001 From: Kari Matthews Date: Fri, 25 Jan 2019 11:55:02 +1100 Subject: [PATCH] Minor bigfixes Prefer const to var Fis whitespace check on strikethrough Use 2 spaces for indenting Comment out citation test Standardize formatting Use let rather than const where appropriate Fix markdown to jira strikethrough Do not transform intra-word underscores Do not transform intra-word asterisks Remove citation from example blob to fix integration test Prevent intra-word formatting in the markdown to jira direction Handle multpile sections of bold and italic in a string Handle multiple codeblocks in the jira to markdown direction Fix tests to expect that formating should not be applied within codeblocks and {noformat} blocks Handle edge cases for bold and italic formatting Use nested describe blocks to make tests clearer Add an underscore in front of unused arguments Improve emphasis formatting from markdown to Jira Add unit tests for urls and emails Get rid of Makefile Prevent further formatting being performed within codeblocks and noformat blocks Update package.json Add FAQ section to README Add unit test for md2jira multiple codeblocks Remove unnecessary comment Make J2M a 'Class' for readability Remove stale code Require a specific direction when transforming MD Update references --- Makefile | 5 - README.md | 30 +++-- index.js | 337 ++++++++++++++++++++++++++++------------------ package-lock.json | 217 +++++++++++++++++++++++++++++ package.json | 8 +- test/jira2md.js | 316 ++++++++++++++++++++++++++++--------------- test/md2jira.js | 290 ++++++++++++++++++++++++++------------- test/test.jira | 6 +- test/test.md | 12 +- 9 files changed, 860 insertions(+), 361 deletions(-) delete mode 100644 Makefile create mode 100644 package-lock.json 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.