diff --git a/__tests__/ExpensiMark-HTML-test.js b/__tests__/ExpensiMark-HTML-test.js index a98e35aa..6adf8420 100644 --- a/__tests__/ExpensiMark-HTML-test.js +++ b/__tests__/ExpensiMark-HTML-test.js @@ -609,6 +609,60 @@ test('Test a url ending with a closing parentheses autolinks correctly', () => { expect(parser.replace(testString)).toBe(resultString); }); +test('Test urls autolinks correctly', () => { + let testString = 'test@expensify.com https://www.expensify.com\n' + + 'test@expensify.com-https://www.expensify.com\n' + + 'test@expensify.com/https://www.expensify.com\n' + + 'test@expensify.com?https://www.expensify.com\n' + + 'test@expensify.com>https://www.expensify.com\n' + + 'https://staging.new.expensify.com/details/test@expensify.com\n' + + 'staging.new.expensify.com/details\n\n' + + 'https://www.expensify.com?name=test&email=test@expensify.com\n' + + 'https://staging.new.expensify.com/details?login=testing@gmail.com\n' + + 'staging.new.expensify.com/details?login=testing@gmail.com\n' + + 'http://necolas.github.io/react-native-web/docs/?path=/docs/components-pressable--disabled\n' + + '-https://www.expensify.com /https://www.expensify.com @https://www.expensify.com\n' + + 'expensify.com -expensify.com @expensify.com\n' + + 'https//www.expensify.com\n' + + '//www.expensify.com?name=test&email=test@expensify.com\n' + + '//staging.new.expensify.com/details?login=testing@gmail.com\n' + + '/details?login=testing@gmail.com\n' + + '?name=test&email=test@expensify.com\n\n' + + 'example.com/https://www.expensify.com\n' + + 'test@gmail.com staging.new.expensify.com/details?login=testing@gmail.com&redirectUrl=https://google.com\n' + + 'test@gmail.com //staging.new.expensify.com/details?login=testing@gmail.com&redirectUrl=https://google.com\n' + + 'test@gmail.com-https://staging.new.expensify.com/details?login=testing@gmail.com&redirectUrl=https://google.com\n' + + 'test@gmail.com/https://example.com/google@email.com?email=asd@email.com\n' + + 'test@gmail.com/test@gmail.com/https://example.com/google@email.com?email=asd@email.com'; + + let resultString = 'test@expensify.com https://www.expensify.com
' + + 'test@expensify.com-https://www.expensify.com
' + + 'test@expensify.com/https://www.expensify.com
' + + 'test@expensify.com?https://www.expensify.com
' + + 'test@expensify.com>https://www.expensify.com
' + + 'https://staging.new.expensify.com/details/test@expensify.com
' + + 'staging.new.expensify.com/details

'+ + 'https://www.expensify.com?name=test&email=test@expensify.com
' + + 'https://staging.new.expensify.com/details?login=testing@gmail.com
' + + 'staging.new.expensify.com/details?login=testing@gmail.com
' + + 'http://necolas.github.io/react-native-web/docs/?path=/docs/components-pressable--disabled
' + + '-https://www.expensify.com /https://www.expensify.com @https://www.expensify.com
' + + 'expensify.com -expensify.com @expensify.com
' + + 'https//www.expensify.com
' + + '//www.expensify.com?name=test&email=test@expensify.com
' + + '//staging.new.expensify.com/details?login=testing@gmail.com
' + + '/details?login=testing@gmail.com
' + + '?name=test&email=test@expensify.com

' + + 'example.com/https://www.expensify.com
' + + 'test@gmail.com staging.new.expensify.com/details?login=testing@gmail.com&redirectUrl=https://google.com
' + + 'test@gmail.com //staging.new.expensify.com/details?login=testing@gmail.com&redirectUrl=https://google.com
' + + 'test@gmail.com-https://staging.new.expensify.com/details?login=testing@gmail.com&redirectUrl=https://google.com
' + + 'test@gmail.com/https://example.com/google@email.com?email=asd@email.com
' + + 'test@gmail.com/test@gmail.com/https://example.com/google@email.com?email=asd@email.com'; + + expect(parser.replace(testString)).toBe(resultString); +}); + test('Test markdown style email link with various styles', () => { const testString = 'Go to ~[Expensify](concierge@expensify.com)~ ' + '_[Expensify](concierge@expensify.com)_ ' diff --git a/lib/ExpensiMark.js b/lib/ExpensiMark.js index 0c34246f..255534bc 100644 --- a/lib/ExpensiMark.js +++ b/lib/ExpensiMark.js @@ -446,12 +446,6 @@ export default class ExpensiMark { let startIndex = 0; while (match !== null) { - // we want to avoid matching email address domains - let abort = false; - if ((match.index !== 0) && (textToCheck[match.index - 1] === '@')) { - abort = true; - } - // we want to avoid matching ending ) unless it is a closing parenthesis for the URL if (textToCheck[(match.index + match[2].length) - 1] === ')' && !match[2].includes('(')) { match[0] = match[0].substr(0, match[0].length - 1); @@ -476,9 +470,28 @@ export default class ExpensiMark { } replacedText = replacedText.concat(textToCheck.substr(startIndex, (match.index - startIndex))); + // we want to avoid matching email address domains + let abort = false; + let shouldRetryByAtSign = false; + + if ((match.index !== 0) && (textToCheck[match.index - 1] === '@')) { + const domainRegex = new RegExp('^(([a-z-0-9]+\\.)+[a-z]{2,})(\\S*)', 'i'); + const domainMatch = domainRegex.exec(match[2]); + + // e.g. test@expensify.com/https://www.test.com + // If the matched string faces @ sign before it, + // We will retry to apply autolink rule to the string(e.g. /https://www.test.com) except for domain(e.g. expensify.com) after @ sign. + if ((domainMatch !== null) && (domainMatch[3] !== '')) { + shouldRetryByAtSign = true; + replacedText = replacedText.concat(domainMatch[1] + this.replace(domainMatch[3], {filterRules: ['autolink']})); + } else { + abort = true; + } + } + if (abort || match[1].includes('
')) {
                 replacedText = replacedText.concat(textToCheck.substr(match.index, (match[0].length)));
-            } else {
+            } else if (!shouldRetryByAtSign) {
                 const urlRegex = new RegExp(`^${LOOSE_URL_REGEX}$|^${URL_REGEX}$`, 'i');
 
                 // `match[1]` contains the text inside the [] of the markdown e.g. [example](https://example.com)