diff --git a/.changeset/curly-weeks-talk.md b/.changeset/curly-weeks-talk.md new file mode 100644 index 00000000000..b8de025d13c --- /dev/null +++ b/.changeset/curly-weeks-talk.md @@ -0,0 +1,5 @@ +--- +"@rocket.chat/meteor": patch +--- + +Fixes emojis being parsed inside code blocks in text message attachment using `chat.postMessage` API call diff --git a/apps/meteor/app/emoji/client/emojiParser.ts b/apps/meteor/app/emoji/client/emojiParser.ts index ff4a010bed5..c919642d283 100644 --- a/apps/meteor/app/emoji/client/emojiParser.ts +++ b/apps/meteor/app/emoji/client/emojiParser.ts @@ -23,6 +23,14 @@ export const emojiParser = (html: string) => { const emojis = Array.from(checkEmojiOnly.querySelectorAll('.emoji:not(:empty), .emojione:not(:empty)')); + emojis.forEach((emojiElement) => { + const htmlElement = emojiElement.parentElement; + + if (htmlElement && htmlElement.nodeName === 'CODE') { + emojiElement.replaceWith(emojiElement.getAttribute('title') ?? ''); + } + }); + let hasText = false; if (!isIE11) { @@ -52,10 +60,11 @@ export const emojiParser = (html: string) => { const { classList } = emojis[i]; classList.add('big'); } - html = checkEmojiOnly.innerHTML; } } + html = checkEmojiOnly.innerHTML; + // apostrophe (') back to ' html = html.replace(/\'/g, '''); diff --git a/apps/meteor/client/components/message/content/attachments/structure/AttachmentText.tsx b/apps/meteor/client/components/message/content/attachments/structure/AttachmentText.tsx index 29530b38b7c..75153d1d57e 100644 --- a/apps/meteor/client/components/message/content/attachments/structure/AttachmentText.tsx +++ b/apps/meteor/client/components/message/content/attachments/structure/AttachmentText.tsx @@ -3,6 +3,8 @@ import type { ComponentPropsWithoutRef } from 'react'; type AttachmentTextProps = ComponentPropsWithoutRef; -const AttachmentText = (props: AttachmentTextProps) => ; +const AttachmentText = (props: AttachmentTextProps) => ( + +); export default AttachmentText; diff --git a/apps/meteor/tests/e2e/messaging.spec.ts b/apps/meteor/tests/e2e/messaging.spec.ts index 9cd0c996993..e02f5b3bcf1 100644 --- a/apps/meteor/tests/e2e/messaging.spec.ts +++ b/apps/meteor/tests/e2e/messaging.spec.ts @@ -1,3 +1,4 @@ +import { faker } from '@faker-js/faker'; import type { Page } from '@playwright/test'; import { createAuxContext } from './fixtures/createAuxContext'; @@ -8,7 +9,7 @@ import { expect, test } from './utils/test'; test.use({ storageState: Users.user1.state }); -test.describe.serial('Messaging', () => { +test.describe('Messaging', () => { let poHomeChannel: HomeChannel; let targetChannel: string; @@ -22,121 +23,195 @@ test.describe.serial('Messaging', () => { await page.goto('/home'); }); - test('should navigate on messages using keyboard', async ({ page }) => { - await poHomeChannel.sidenav.openChat(targetChannel); - await poHomeChannel.content.sendMessage('msg1'); - await poHomeChannel.content.sendMessage('msg2'); + test.describe.serial('Navigation', () => { + test('should navigate on messages using keyboard', async ({ page }) => { + await poHomeChannel.sidenav.openChat(targetChannel); + await poHomeChannel.content.sendMessage('msg1'); + await poHomeChannel.content.sendMessage('msg2'); - // move focus to the second message - await page.keyboard.press('Shift+Tab'); - await expect(page.locator('[data-qa-type="message"]').last()).toBeFocused(); + // move focus to the second message + await page.keyboard.press('Shift+Tab'); + await expect(page.locator('[data-qa-type="message"]').last()).toBeFocused(); - // move focus to the first system message - await page.keyboard.press('ArrowUp'); - await page.keyboard.press('ArrowUp'); - await expect(page.locator('[data-qa="system-message"]').first()).toBeFocused(); + // move focus to the first system message + await page.keyboard.press('ArrowUp'); + await page.keyboard.press('ArrowUp'); + await expect(page.locator('[data-qa="system-message"]').first()).toBeFocused(); - // move focus to the first typed message - await page.keyboard.press('ArrowDown'); - await expect(page.locator('[data-qa-type="message"]:has-text("msg1")')).toBeFocused(); + // move focus to the first typed message + await page.keyboard.press('ArrowDown'); + await expect(page.locator('[data-qa-type="message"]:has-text("msg1")')).toBeFocused(); - // move focus to the room title - await page.keyboard.press('Shift+Tab'); - await expect(page.getByRole('button', { name: targetChannel }).first()).toBeFocused(); + // move focus to the room title + await page.keyboard.press('Shift+Tab'); + await expect(page.getByRole('button', { name: targetChannel }).first()).toBeFocused(); - // refocus on the first typed message - await page.keyboard.press('Tab'); - await page.keyboard.press('Tab'); - await page.keyboard.press('Tab'); - await expect(page.locator('[data-qa-type="message"]:has-text("msg1")')).toBeFocused(); + // refocus on the first typed message + await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); + await expect(page.locator('[data-qa-type="message"]:has-text("msg1")')).toBeFocused(); - // move focus to the message toolbar - await page - .locator('[data-qa-type="message"]:has-text("msg1")') - .locator('[role=toolbar][aria-label="Message actions"]') - .getByRole('button', { name: 'Add reaction' }) - .waitFor(); - - await page.keyboard.press('Tab'); - await page.keyboard.press('Tab'); - await expect( - page + // move focus to the message toolbar + await page .locator('[data-qa-type="message"]:has-text("msg1")') .locator('[role=toolbar][aria-label="Message actions"]') - .getByRole('button', { name: 'Add reaction' }), - ).toBeFocused(); + .getByRole('button', { name: 'Add reaction' }) + .waitFor(); - // move focus to the composer - await page.keyboard.press('Tab'); - await page - .locator('[data-qa-type="message"]:has-text("msg2")') - .locator('[role=toolbar][aria-label="Message actions"]') - .getByRole('button', { name: 'Add reaction' }) - .waitFor(); - await page.keyboard.press('Tab'); - await page.keyboard.press('Tab'); - await expect(poHomeChannel.composer).toBeFocused(); + await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); + await expect( + page + .locator('[data-qa-type="message"]:has-text("msg1")') + .locator('[role=toolbar][aria-label="Message actions"]') + .getByRole('button', { name: 'Add reaction' }), + ).toBeFocused(); + + // move focus to the composer + await page.keyboard.press('Tab'); + await page + .locator('[data-qa-type="message"]:has-text("msg2")') + .locator('[role=toolbar][aria-label="Message actions"]') + .getByRole('button', { name: 'Add reaction' }) + .waitFor(); + await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); + await expect(poHomeChannel.composer).toBeFocused(); + }); + + test('should navigate properly on the user card', async ({ page }) => { + await poHomeChannel.sidenav.openChat(targetChannel); + + // open UserCard + await page.keyboard.press('Shift+Tab'); + await page.keyboard.press('ArrowUp'); + await page.keyboard.press('Tab'); + await page.keyboard.press('Space'); + await expect(poHomeChannel.userCardToolbar).toBeVisible(); + + // close UserCard with Esc + await page.keyboard.press('Escape'); + await expect(poHomeChannel.userCardToolbar).not.toBeVisible(); + + // with focus restored reopen toolbar + await page.keyboard.press('Space'); + await expect(poHomeChannel.userCardToolbar).toBeVisible(); + + // close UserCard with button + await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); + await page.keyboard.press('Space'); + await expect(poHomeChannel.userCardToolbar).not.toBeVisible(); + }); + + test('should not restore focus on the last focused if it was triggered by click', async ({ page }) => { + await poHomeChannel.sidenav.openChat(targetChannel); + await page.locator('[data-qa-type="message"]:has-text("msg1")').click(); + await poHomeChannel.composer.click(); + await page.keyboard.press('Shift+Tab'); + + await expect(page.locator('[data-qa-type="message"]:has-text("msg2")')).toBeFocused(); + }); + + test('should not focus on the last message when focusing by click', async ({ page }) => { + await poHomeChannel.sidenav.openChat(targetChannel); + await page.locator('[data-qa-type="message"]:has-text("msg1")').click(); + + await expect(page.locator('[data-qa-type="message"]').last()).not.toBeFocused(); + }); + + test('should focus the latest message when moving the focus on the list and theres no previous focus', async ({ page }) => { + await poHomeChannel.sidenav.openChat(targetChannel); + await page.getByRole('button', { name: targetChannel }).first().focus(); + + // move focus to the list + await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); + await expect(page.locator('[data-qa-type="message"]').last()).toBeFocused(); + + await page.getByRole('button', { name: targetChannel }).first().focus(); + + // move focus to the list again + await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); + await page.keyboard.press('Tab'); + await expect(page.locator('[data-qa-type="message"]').last()).toBeFocused(); + }); }); - test('should navigate properly on the user card', async ({ page }) => { - await poHomeChannel.sidenav.openChat(targetChannel); + test.describe('Message by "chat.postMessage" API method', () => { + test('expect show a message', async ({ api }) => { + const messageText = faker.lorem.sentence(); - // open UserCard - await page.keyboard.press('Shift+Tab'); - await page.keyboard.press('ArrowUp'); - await page.keyboard.press('Tab'); - await page.keyboard.press('Space'); - await expect(poHomeChannel.userCardToolbar).toBeVisible(); + await poHomeChannel.sidenav.openChat(targetChannel); - // close UserCard with Esc - await page.keyboard.press('Escape'); - await expect(poHomeChannel.userCardToolbar).not.toBeVisible(); + await api.post('/chat.postMessage', { + channel: targetChannel, + text: messageText, + }); - // with focus restored reopen toolbar - await page.keyboard.press('Space'); - await expect(poHomeChannel.userCardToolbar).toBeVisible(); + await expect(poHomeChannel.content.lastUserMessageBody).toHaveText(messageText); + }); - // close UserCard with button - await page.keyboard.press('Tab'); - await page.keyboard.press('Tab'); - await page.keyboard.press('Tab'); - await page.keyboard.press('Space'); - await expect(poHomeChannel.userCardToolbar).not.toBeVisible(); - }); + test('expect show attachment text', async ({ api }) => { + const messageText = faker.lorem.sentence(); + const attachmentText = faker.lorem.sentence(); - test('should not restore focus on the last focused if it was triggered by click', async ({ page }) => { - await poHomeChannel.sidenav.openChat(targetChannel); - await page.locator('[data-qa-type="message"]:has-text("msg1")').click(); - await poHomeChannel.composer.click(); - await page.keyboard.press('Shift+Tab'); + await poHomeChannel.sidenav.openChat(targetChannel); - await expect(page.locator('[data-qa-type="message"]:has-text("msg2")')).toBeFocused(); - }); + await api.post('/chat.postMessage', { + channel: targetChannel, + text: messageText, + attachments: [ + { + text: attachmentText, + }, + ], + }); - test('should not focus on the last message when focusing by click', async ({ page }) => { - await poHomeChannel.sidenav.openChat(targetChannel); - await page.locator('[data-qa-type="message"]:has-text("msg1")').click(); + await expect(poHomeChannel.content.lastUserMessageAttachment).toHaveText(attachmentText); + }); - await expect(page.locator('[data-qa-type="message"]').last()).not.toBeFocused(); - }); + test('expect show attachment text with emoji', async ({ api }) => { + const messageText = faker.lorem.sentence(); + const attachmentText = faker.lorem.sentence(); - test('should focus the latest message when moving the focus on the list and theres no previous focus', async ({ page }) => { - await poHomeChannel.sidenav.openChat(targetChannel); - await page.getByRole('button', { name: targetChannel }).first().focus(); + await poHomeChannel.sidenav.openChat(targetChannel); - // move focus to the list - await page.keyboard.press('Tab'); - await page.keyboard.press('Tab'); - await page.keyboard.press('Tab'); - await expect(page.locator('[data-qa-type="message"]').last()).toBeFocused(); + await api.post('/chat.postMessage', { + channel: targetChannel, + text: messageText, + attachments: [ + { + text: `${attachmentText} B) `, + }, + ], + }); - await page.getByRole('button', { name: targetChannel }).first().focus(); + await expect(poHomeChannel.content.lastUserMessageAttachment).toHaveText(`${attachmentText} \ud83d\ude0e `); + }); - // move focus to the list again - await page.keyboard.press('Tab'); - await page.keyboard.press('Tab'); - await page.keyboard.press('Tab'); - await expect(page.locator('[data-qa-type="message"]').last()).toBeFocused(); + test('expect show attachment text without emoji inside code block', async ({ api }) => { + const messageText = faker.lorem.sentence(); + const attachmentText = faker.lorem.sentence(); + + await poHomeChannel.sidenav.openChat(targetChannel); + + await api.post('/chat.postMessage', { + channel: targetChannel, + text: messageText, + attachments: [ + { + text: `\`\`\`${attachmentText} B) \`\`\``, + }, + ], + }); + + await expect(poHomeChannel.content.lastUserMessageAttachment).toHaveText(`${attachmentText} B) `); + }); }); test.describe('Both contexts', () => { diff --git a/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts b/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts index f60b8fbcc34..8cfa1ecae9a 100644 --- a/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts +++ b/apps/meteor/tests/e2e/page-objects/fragments/home-content.ts @@ -47,6 +47,10 @@ export class HomeContent { return this.lastUserMessage.locator('[data-qa-type="message-body"]'); } + get lastUserMessageAttachment(): Locator { + return this.page.locator('[data-qa-type="message-attachment"]').last(); + } + get lastUserMessageNotSequential(): Locator { return this.page.locator('[data-qa-type="message"][data-sequential="false"]').last(); } diff --git a/yarn.lock b/yarn.lock index c5bd3ea4b1d..c30c286c2e0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8224,7 +8224,7 @@ __metadata: storybook-dark-mode: "npm:^4.0.2" typescript: "npm:~5.7.2" peerDependencies: - "@rocket.chat/apps-engine": 1.51.0-rc.0 + "@rocket.chat/apps-engine": 1.51.0 "@rocket.chat/eslint-config": 0.7.0 "@rocket.chat/fuselage": "*" "@rocket.chat/fuselage-hooks": "*"