diff --git a/__tests__/ExpensiMark-HTML-test.js b/__tests__/ExpensiMark-HTML-test.js
index d31b5dee..1a137162 100644
--- a/__tests__/ExpensiMark-HTML-test.js
+++ b/__tests__/ExpensiMark-HTML-test.js
@@ -23,8 +23,8 @@ test('Test multi-line bold markdown replacement', () => {
test('Test bold within code blocks is skipped', () => {
- const testString = 'bold\n```*not a bold*```\nThis is *bold*';
- const replacedString = 'bold
*not a bold*This is bold'; + const testString = 'bold\n```\n*not a bold*\n```\nThis is *bold*'; + const replacedString = 'bold
*not a bold*This is bold'; expect(parser.replace(testString)).toBe(replacedString); }); @@ -202,14 +202,14 @@ test('Test markdown replacement for emails wrapped in bold/strikethrough/italic // Check emails within other markdown test('Test emails within other markdown', () => { const testString = '> test@example.com\n' - + '```test@example.com```\n' + + '```\ntest@example.com\n```\n' + '`test@example.com`\n' + '_test@example.com_ ' + '_test@example.com__ ' + '__test@example.com__ ' + '__test@example.com_'; const result = '
test@example.com' - + '
test@example.com' + + '
test@example.com' + '
test@example.com
const javaScript = 'javaScript''); - - codeFenceExampleMarkdown = '```const javaScript = \'javaScript\'\n```'; - expect(parser.replace(codeFenceExampleMarkdown)).toBe('
const javaScript = 'javaScript''); - - codeFenceExampleMarkdown = '```\nconst javaScript = \'javaScript\'```'; - expect(parser.replace(codeFenceExampleMarkdown)).toBe('
const javaScript = 'javaScript''); - - codeFenceExampleMarkdown = '```const javaScript = \'javaScript\'```'; - expect(parser.replace(codeFenceExampleMarkdown)).toBe('
const javaScript = 'javaScript''); }); test('Test code fencing with spaces and new lines', () => { let codeFenceExample = '```\nconst javaScript = \'javaScript\'\n const php = \'php\'\n```'; expect(parser.replace(codeFenceExample)).toBe('
const javaScript = 'javaScript''); - codeFenceExample = '```const javaScript = \'javaScript\'\n const php = \'php\'\n```'; - expect(parser.replace(codeFenceExample)).toBe('
const php = 'php'
const javaScript = 'javaScript''); - - codeFenceExample = '```\nconst javaScript = \'javaScript\'\n const php = \'php\'```'; - expect(parser.replace(codeFenceExample)).toBe('
const php = 'php'
const javaScript = 'javaScript''); - - codeFenceExample = '```const javaScript = \'javaScript\'\n const php = \'php\'```'; - expect(parser.replace(codeFenceExample)).toBe('
const php = 'php'
const javaScript = 'javaScript''); - codeFenceExample = '```\n\n# test\n\n```'; expect(parser.replace(codeFenceExample)).toBe('
const php = 'php'
'); @@ -457,6 +439,21 @@ test('Test inline code blocks', () => { expect(parser.replace(inlineCodeStartString)).toBe('My favorite language is
# test
JavaScript
. How about you?');
});
+test('Test inline code blocks with double backticks', () => {
+ const inlineCodeStartString = 'My favorite language is ``JavaScript``. How about you?';
+ expect(parser.replace(inlineCodeStartString)).toBe('My favorite language is `JavaScript`
. How about you?');
+});
+
+test('Test inline code blocks with triple backticks', () => {
+ const inlineCodeStartString = 'My favorite language is ```JavaScript```. How about you?';
+ expect(parser.replace(inlineCodeStartString)).toBe('My favorite language is ``JavaScript``
. How about you?');
+});
+
+test('Test multiple inline codes in one line', () => {
+ const inlineCodeStartString = 'My favorite language is `JavaScript`. How about you? I also like `Python`.';
+ expect(parser.replace(inlineCodeStartString)).toBe('My favorite language is JavaScript
. How about you? I also like Python
.');
+});
+
test('Test inline code with one backtick as content', () => {
const inlineCodeStartString = '```';
expect(parser.replace(inlineCodeStartString)).toBe('```');
@@ -488,58 +485,46 @@ test('Test inline code blocks with two backticks', () => {
});
test('Test code fencing with ExpensiMark syntax inside', () => {
- let codeFenceExample = '```\nThis is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)\n```';
+ const codeFenceExample = '```\nThis is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)\n```';
expect(parser.replace(codeFenceExample)).toBe('This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)'); - - codeFenceExample = '```This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)\n```'; - expect(parser.replace(codeFenceExample)).toBe('
This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)'); - - codeFenceExample = '```\nThis is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)```'; - expect(parser.replace(codeFenceExample)).toBe('
This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)'); - - codeFenceExample = '```This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)```'; - expect(parser.replace(codeFenceExample)).toBe('
This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)'); }); test('Test code fencing with ExpensiMark syntax outside', () => { - let codeFenceExample = '# Test1 ```code``` Test2'; - expect(parser.replace(codeFenceExample)).toBe('
codeTest2'); + let codeFenceExample = '# Test1 ```\ncode\n``` Test2'; + expect(parser.replace(codeFenceExample)).toBe('
codeTest2'); - codeFenceExample = '*Test1 ```code``` Test2*'; - expect(parser.replace(codeFenceExample)).toBe('*Test1
codeTest2*'); - expect(parser.replace(codeFenceExample, {shouldKeepRawInput: true})).toBe('*Test1
codeTest2*'); + codeFenceExample = '*Test1 ```\ncode\n``` Test2*'; + expect(parser.replace(codeFenceExample)).toBe('*Test1
codeTest2*'); + expect(parser.replace(codeFenceExample, {shouldKeepRawInput: true})).toBe('*Test1
code\nTest2*'); - codeFenceExample = '_Test1 ```code``` Test2_'; - expect(parser.replace(codeFenceExample)).toBe('_Test1
codeTest2_'); - expect(parser.replace(codeFenceExample, {shouldKeepRawInput: true})).toBe('_Test1
codeTest2_'); + codeFenceExample = '_Test1 ```\ncode\n``` Test2_'; + expect(parser.replace(codeFenceExample)).toBe('_Test1
codeTest2_'); + expect(parser.replace(codeFenceExample, {shouldKeepRawInput: true})).toBe('_Test1
code\nTest2_'); - codeFenceExample = '~Test1 ```code``` Test2~'; - expect(parser.replace(codeFenceExample)).toBe('~Test1
codeTest2~'); - expect(parser.replace(codeFenceExample, {shouldKeepRawInput: true})).toBe('~Test1
codeTest2~'); + codeFenceExample = '~Test1 ```\ncode\n``` Test2~'; + expect(parser.replace(codeFenceExample)).toBe('~Test1
codeTest2~'); + expect(parser.replace(codeFenceExample, {shouldKeepRawInput: true})).toBe('~Test1
code\nTest2~'); - codeFenceExample = '[```code```](google.com)'; - expect(parser.replace(codeFenceExample)).toBe('[
code](google.com)'); - expect(parser.replace(codeFenceExample, {shouldKeepRawInput: true})).toBe('[
code](google.com)'); + codeFenceExample = '[```\ncode\n```](google.com)'; + expect(parser.replace(codeFenceExample)).toBe('[
code](google.com)'); + expect(parser.replace(codeFenceExample, {shouldKeepRawInput: true})).toBe('[
code\n](google.com)'); }); test('Test code fencing with additional backticks inside', () => { - let nestedBackticks = '````test````'; - expect(parser.replace(nestedBackticks)).toBe('
`test`'); - - nestedBackticks = '````\ntest\n````'; - expect(parser.replace(nestedBackticks)).toBe('
`'); + let nestedBackticks = '```\n`test`\n```'; + expect(parser.replace(nestedBackticks)).toBe('
test
`
`test`'); - nestedBackticks = '````````'; - expect(parser.replace(nestedBackticks)).toBe('
``'); + nestedBackticks = '```\n`\ntest\n`\n```'; + expect(parser.replace(nestedBackticks)).toBe('
`'); - nestedBackticks = '````\n````'; - expect(parser.replace(nestedBackticks)).toBe('
test
`
`'); + nestedBackticks = '```\n``\n```'; + expect(parser.replace(nestedBackticks)).toBe('
`
``'); - nestedBackticks = '```````````'; - expect(parser.replace(nestedBackticks)).toBe('
`````'); + nestedBackticks = '```\n`\n`\n```'; + expect(parser.replace(nestedBackticks)).toBe('
`'); - nestedBackticks = '````This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)````'; - expect(parser.replace(nestedBackticks)).toBe('
`
`This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)`'); + nestedBackticks = '```\n`This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)`\n```'; + expect(parser.replace(nestedBackticks)).toBe('
`This is how you can write ~strikethrough~, *bold*, _italics_, and [links](https://www.expensify.com)`'); }); test('Test combination replacements', () => { @@ -1016,41 +1001,41 @@ test('Test autolink replacement to avoid parsing nested links', () => { }); test('Test quotes markdown replacement with text matching inside and outside codefence without spaces', () => { - const testString = 'The next line should be quoted\n> Hello,I’mtext\n```\nThe next line should not be quoted\n>Hello,I’mtext\nsince its inside a codefence```'; + const testString = 'The next line should be quoted\n> Hello,I’mtext\n```\nThe next line should not be quoted\n>Hello,I’mtext\nsince its inside a codefence\n```'; - const resultString = 'The next line should be quoted
Hello,I’mtext
The next line should not be quoted'; + const resultString = 'The next line should be quoted
>Hello,I’mtext
since its inside a codefence
Hello,I’mtext
The next line should not be quoted'; expect(parser.replace(testString)).toBe(resultString); }); test('Test quotes markdown replacement with text matching inside and outside codefence at the same line', () => { - const testString = 'The next line should be quoted\n> Hello,I’mtext\nThe next line should not be quoted\n```>Hello,I’mtext```\nsince its inside a codefence'; + const testString = 'The next line should be quoted\n> Hello,I’mtext\nThe next line should not be quoted\n```\n>Hello,I’mtext\n```\nsince its inside a codefence'; - const resultString = 'The next line should be quoted
>Hello,I’mtext
since its inside a codefence
Hello,I’mtextThe next line should not be quoted
>Hello,I’mtextsince its inside a codefence'; + const resultString = 'The next line should be quoted
Hello,I’mtextThe next line should not be quoted
>Hello,I’mtextsince its inside a codefence'; expect(parser.replace(testString)).toBe(resultString); }); test('Test quotes markdown replacement with text matching inside and outside codefence at the end of the text', () => { - const testString = 'The next line should be quoted\n> Hello,I’mtext\nThe next line should not be quoted\n```>Hello,I’mtext```'; + const testString = 'The next line should be quoted\n> Hello,I’mtext\nThe next line should not be quoted\n```\n>Hello,I’mtext\n```'; - const resultString = 'The next line should be quoted
Hello,I’mtextThe next line should not be quoted
>Hello,I’mtext'; + const resultString = 'The next line should be quoted
Hello,I’mtextThe next line should not be quoted
>Hello,I’mtext'; expect(parser.replace(testString)).toBe(resultString); }); test('Test quotes markdown replacement with text matching inside and outside codefence with quotes at the end of the text', () => { - const testString = 'The next line should be quoted\n```> Hello,I’mtext```\nThe next line should not be quoted\n> Hello,I’mtext'; + const testString = 'The next line should be quoted\n```\n> Hello,I’mtext\n```\nThe next line should not be quoted\n> Hello,I’mtext'; - const resultString = 'The next line should be quoted
> Hello,I’mtextThe next line should not be quoted
Hello,I’mtext'; + const resultString = 'The next line should be quoted
> Hello,I’mtextThe next line should not be quoted
Hello,I’mtext'; expect(parser.replace(testString)).toBe(resultString); }); test('Test quotes markdown replacement and removing
and
>Hello,I’mtextThe next line should not be quoted'; + const resultString = 'The next line should be quoted
>Hello,I’mtextThe next line should not be quoted'; expect(parser.replace(testString)).toBe(resultString); }); @@ -1284,8 +1269,8 @@ test('Test for user mention with invalid username', () => { }); test('Test for user mention with codefence style', () => { - const testString = '```@username@expensify.com```'; - const resultString = '
@username@expensify.com'; + const testString = '```\n@username@expensify.com\n```'; + const resultString = '
@username@expensify.com'; expect(parser.replace(testString)).toBe(resultString); }); @@ -1296,8 +1281,8 @@ test('Test for user mention with inlineCodeBlock style', () => { }); test('Test for user mention with text with codefence style', () => { - const testString = '```hi @username@expensify.com```'; - const resultString = '
hi @username@expensify.com'; + const testString = '```\nhi @username@expensify.com\n```'; + const resultString = '
hi @username@expensify.com'; expect(parser.replace(testString)).toBe(resultString); }); @@ -1326,8 +1311,8 @@ test('Test for user mention with user email includes underscores', () => { }); test('Test for @here mention with codefence style', () => { - const testString = '```@here```'; - const resultString = '
@here'; + const testString = '```\n@here\n```'; + const resultString = '
@here'; expect(parser.replace(testString)).toBe(resultString); }); @@ -1589,11 +1574,11 @@ test('Test here mention with @here@here', () => { }); test('Test link with code fence inside the alias text part', () => { - const testString = '[```code```](google.com) ' - + '[test ```code``` test](google.com)'; + const testString = '[```\ncode\n```](google.com) ' + + '[test ```\ncode\n``` test](google.com)'; - const resultString = '[
code](google.com) ' - + '[test
codetest](google.com)'; + const resultString = '[
code](google.com) ' + + '[test
codetest](google.com)'; expect(parser.replace(testString)).toBe(resultString); }); @@ -1635,20 +1620,20 @@ test('Linebreak between end of text and start of code block should be remained', resultString: '|
code', }, { - testString: 'text1```code```text2', - resultString: 'text1
codetext2', + testString: 'text1```\ncode\n```text2', + resultString: 'text1
codetext2', }, { - testString: 'text1 ``` code ``` text2', - resultString: 'text1
codetext2', + testString: 'text1 ```\n code \n``` text2', + resultString: 'text1
codetext2', }, { - testString: 'text1\n```code```\ntext2', - resultString: 'text1
codetext2', + testString: 'text1\n```\ncode\n```\ntext2', + resultString: 'text1
codetext2', }, { - testString: 'text1\n``` code ```\ntext2', - resultString: 'text1
codetext2', + testString: 'text1\n```\n code \n```\ntext2', + resultString: 'text1
codetext2', }, { testString: 'text1\n```\n\ncode\n```\ntext2', @@ -1674,24 +1659,24 @@ test('Linebreak between end of text and start of code block should be remained', test('Test autoEmail with markdown of
, , , and tag', () => {
const testString = '`code`test@gmail.com '
- + '```code block```test@gmail.com '
+ + '```\ncode block\n```test@gmail.com '
+ '[Google](https://google.com)test@gmail.com '
+ '_test@gmail.com_ '
+ '_test\n\ntest@gmail.com_ '
+ '`test@expensify.com` '
- + '```test@expensify.com``` '
+ + '```\ntest@expensify.com\n``` '
+ '@test@expensify.com '
+ '_@username@expensify.com_ '
+ '[https://staging.new.expensify.com/details/test@expensify.com](https://staging.new.expensify.com/details/test@expensify.com) '
+ '[test italic style wrap email _test@gmail.com_ inside a link](https://google.com) ';
const resultString = 'code
test@gmail.com '
- + 'code block
test@gmail.com '
+ + 'code block
test@gmail.com '
+ 'Googletest@gmail.com '
+ 'test@gmail.com '
+ 'test
test@gmail.com '
+ 'test@expensify.com
'
- + 'test@expensify.com
'
+ + 'test@expensify.com
'
+ '@test@expensify.com '
+ '@username@expensify.com '
+ 'https://staging.new.expensify.com/details/test@expensify.com '
@@ -1852,20 +1837,20 @@ describe('when should keep raw input flag is enabled', () => {
});
test('Test code fence within inline code', () => {
- let testString = 'Hello world `(```test```)` Hello world';
- expect(parser.replace(testString)).toBe('Hello world `(test
)` Hello world');
+ let testString = 'Hello world `(```\ntest\n```)` Hello world';
+ expect(parser.replace(testString)).toBe('Hello world `(test
)` Hello world');
- testString = 'Hello world `(```test\ntest```)` Hello world';
- expect(parser.replace(testString)).toBe('Hello world `(test
test
)` Hello world');
+ testString = 'Hello world `(```\ntest\ntest\n```)` Hello world';
+ expect(parser.replace(testString)).toBe('Hello world `(test
test
)` Hello world');
- testString = 'Hello world ```(`test`)``` Hello world';
- expect(parser.replace(testString)).toBe('Hello world (`test`)
Hello world');
+ testString = 'Hello world ```\n(`test`)\n``` Hello world';
+ expect(parser.replace(testString)).toBe('Hello world (`test`)
Hello world');
- testString = 'Hello world `test`space```block``` Hello world';
- expect(parser.replace(testString)).toBe('Hello world test
spaceblock
Hello world');
+ testString = 'Hello world `test`space```\nblock\n``` Hello world';
+ expect(parser.replace(testString)).toBe('Hello world test
spaceblock
Hello world');
- testString = 'Hello world ```block```space`test` Hello world';
- expect(parser.replace(testString)).toBe('Hello world block
spacetest
Hello world');
+ testString = 'Hello world ```\nblock\n```space`test` Hello world';
+ expect(parser.replace(testString)).toBe('Hello world block
spacetest
Hello world');
});
test('Test italic/bold/strikethrough markdown to keep consistency', () => {
diff --git a/lib/ExpensiMark.js b/lib/ExpensiMark.js
index 4b8a7eb2..05065558 100644
--- a/lib/ExpensiMark.js
+++ b/lib/ExpensiMark.js
@@ -32,7 +32,7 @@ export default class ExpensiMark {
name: 'codeFence',
// ` is a backtick symbol we are matching on three of them before then after a new line character
- regex: /(```(?:\r\n|\n)?)((?:\s*?(?!(?:\r\n|\n)?```(?!`))[\S])+\s*?)((?=(?:\r\n|\n)?)```)/g,
+ regex: /(```(?:\r\n|\n))((?:\s*?(?!(?:\r\n|\n)?```(?!`))[\S])+\s*?(?:\r\n|\n))(```)/g,
// We're using a function here to perform an additional replace on the content
// inside the backticks because Android is not able to use tags and does
@@ -61,16 +61,8 @@ export default class ExpensiMark {
// Use the url escaped version of a backtick (`) symbol. Mobile platforms do not support lookbehinds,
// so capture the first and third group and place them in the replacement.
// but we should not replace backtick symbols if they include tags between them.
- regex: /(\B|_|)`(?:(?!(?:(?!`).)*?))(.*?\S.*?)`(\B|_|)(?!`|[^<]*<\/pre>)/g,
- replacement: (match, g1, g2, g3) => {
- const regex = /^[`]+$/i;
-
- // if content of the inline code block is only backtick symbols, we should not replace them with tag
- if (regex.test(g2)) {
- return match;
- }
- return `${g1}${g2}
${g3}`;
- },
+ regex: /(\B|_|)`(.*?(?![`])\S.*?)`(\B|_|)(?!`|[^<]*<\/pre>)/gm,
+ replacement: '$1$2
$3',
},
/**