diff --git a/test/api/unit/libs/highlightMentions.test.js b/test/api/unit/libs/highlightMentions.test.js index 30fb34c72c..266bfbaaa6 100644 --- a/test/api/unit/libs/highlightMentions.test.js +++ b/test/api/unit/libs/highlightMentions.test.js @@ -47,6 +47,12 @@ describe('highlightMentions', () => { expect(result[0]).to.equal('[@user-dash](/profile/444): message [@user_underscore](/profile/555)'); }); + it('highlights users with case-insensitive matching', async () => { + const text = '@USER: message @User2 @USER3'; + const result = await highlightMentions(text); + expect(result[0]).to.equal('[@USER](/profile/111): message [@User2](/profile/222) [@USER3](/profile/333)'); + }); + it('doesn\'t highlight nonexisting users', async () => { const text = '@nouser message'; const result = await highlightMentions(text); diff --git a/test/api/v3/integration/chat/POST-chat.test.js b/test/api/v3/integration/chat/POST-chat.test.js index ba4ac00ed0..f16cbea6f9 100644 --- a/test/api/v3/integration/chat/POST-chat.test.js +++ b/test/api/v3/integration/chat/POST-chat.test.js @@ -238,6 +238,18 @@ describe('POST /chat', () => { expect(groupMessages[0].id).to.exist; }); + it('creates a chat with case-insensitive mentions', async () => { + const originalUsername = member.auth.local.username; + const uppercaseUsername = originalUsername.toUpperCase(); + const messageWithMentions = `hi @${uppercaseUsername}`; + const newMessage = await user.post(`/groups/${groupWithChat._id}/chat`, { message: messageWithMentions }); + const groupMessages = await user.get(`/groups/${groupWithChat._id}/chat`); + + expect(newMessage.message.id).to.exist; + expect(newMessage.message.text).to.include(`[@${uppercaseUsername}](/profile/${member._id})`); + expect(groupMessages[0].id).to.exist; + }); + it('creates a chat with a max length of 3000 chars', async () => { const veryLongMessage = ` 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789 123456789. diff --git a/website/server/libs/highlightMentions.js b/website/server/libs/highlightMentions.js index ccb074590e..9798b490d0 100644 --- a/website/server/libs/highlightMentions.js +++ b/website/server/libs/highlightMentions.js @@ -164,18 +164,26 @@ export default async function highlightMentions (text) { if (mentions && mentions.length <= 5) { const usernames = mentions.map(mention => mention.substr(1)); + const usernameRegexes = usernames.map(username => new RegExp(`^${escapeRegExp(username)}$`, 'i')); members = await User - .find({ 'auth.local.username': { $in: usernames }, 'flags.verifiedUsername': true }) + .find({ + $or: usernameRegexes.map(regex => ({ 'auth.local.username': regex })), + 'flags.verifiedUsername': true + }) .select(['auth.local.username', '_id', 'preferences.pushNotifications', 'pushDevices', 'party', 'guilds']) .lean() .exec(); const baseUrl = determineBaseUrl(); members.forEach(member => { const { username } = member.auth.local; - const regex = new RegExp(`@${username}(?![\\-\\w])`, 'g'); - const replacement = `[@${username}](${baseUrl}/profile/${member._id})`; + const regex = new RegExp(`@${escapeRegExp(username)}(?![\\-\\w])`, 'gi'); - textBlocks.transformValidBlocks(blockText => blockText.replace(regex, replacement)); + textBlocks.transformValidBlocks(blockText => + blockText.replace(regex, match => { + const mentionedUsername = match.substr(1); + return `[@${mentionedUsername}](${baseUrl}/profile/${member._id})`; + }) + ); }); }