message-group.test.tsx 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250
  1. import { expect } from 'chai'
  2. import { render, screen } from '@testing-library/react'
  3. import MessageGroup from '../../../../../frontend/js/features/chat/components/message-group'
  4. import { stubMathJax, tearDownMathJaxStubs } from './stubs'
  5. import { User, UserId } from '@ol-types/user'
  6. import {
  7. ChatContext,
  8. Message as MessageType,
  9. } from '@/features/chat/context/chat-context'
  10. import { UserProvider } from '@/shared/context/user-context'
  11. import { ModalsContextProvider } from '@/features/ide-react/context/modals-context'
  12. import { SplitTestProvider } from '@/shared/context/split-test-context'
  13. describe('<MessageGroup />', function () {
  14. function ChatProviders({
  15. children,
  16. idOfMessageBeingEdited = null,
  17. }: {
  18. children: React.ReactNode
  19. idOfMessageBeingEdited?: string | null
  20. }) {
  21. const mockContextValue = {
  22. idOfMessageBeingEdited,
  23. cancelMessageEdit: () => {},
  24. editMessage: () => {},
  25. }
  26. return (
  27. <UserProvider>
  28. <ModalsContextProvider>
  29. <SplitTestProvider>
  30. <ChatContext.Provider value={mockContextValue as any}>
  31. {children}
  32. </ChatContext.Provider>
  33. </SplitTestProvider>
  34. </ModalsContextProvider>
  35. </UserProvider>
  36. )
  37. }
  38. const currentUser: User = {
  39. id: 'fake_user' as UserId,
  40. first_name: 'fake_user_first_name',
  41. email: 'fake@example.com',
  42. }
  43. beforeEach(function () {
  44. window.metaAttributesCache.set('ol-user', currentUser)
  45. stubMathJax()
  46. })
  47. afterEach(function () {
  48. tearDownMathJaxStubs()
  49. })
  50. it('renders a basic message', function () {
  51. const message: MessageType = {
  52. content: 'a message',
  53. user: currentUser,
  54. id: 'msg_1',
  55. timestamp: new Date('2025-01-01T00:00:00.000Z').getTime(),
  56. }
  57. render(
  58. <ChatProviders>
  59. <MessageGroup messages={[message]} fromSelf />
  60. </ChatProviders>
  61. )
  62. screen.getByText('a message')
  63. })
  64. it('renders a message with multiple contents', function () {
  65. const messages: MessageType[] = [
  66. {
  67. content: 'a message',
  68. user: currentUser,
  69. id: 'msg_1',
  70. timestamp: new Date('2025-01-01T00:00:00.000Z').getTime(),
  71. },
  72. {
  73. content: 'another message',
  74. user: currentUser,
  75. id: 'msg_2',
  76. timestamp: new Date('2025-01-01T00:00:00.000Z').getTime(),
  77. },
  78. ]
  79. render(
  80. <ChatProviders>
  81. <MessageGroup messages={messages} fromSelf />
  82. </ChatProviders>
  83. )
  84. screen.getByText('a message')
  85. screen.getByText('another message')
  86. })
  87. it('renders HTML links within messages', function () {
  88. const message: MessageType = {
  89. content:
  90. 'a message with a <a href="https://overleaf.com">link to Overleaf</a>',
  91. user: currentUser,
  92. id: 'msg_1',
  93. timestamp: new Date('2025-01-01T00:00:00.000Z').getTime(),
  94. }
  95. render(
  96. <ChatProviders>
  97. <MessageGroup messages={[message]} fromSelf />
  98. </ChatProviders>
  99. )
  100. screen.getByRole('link', { name: 'https://overleaf.com' })
  101. })
  102. it('renders edited message with "(edited)" indicator', function () {
  103. const editedMessage: MessageType = {
  104. content: 'this message has been edited',
  105. user: currentUser,
  106. id: 'msg_1',
  107. timestamp: new Date('2025-01-01T00:00:00.000Z').getTime(),
  108. edited: true,
  109. }
  110. render(
  111. <ChatProviders>
  112. <MessageGroup messages={[editedMessage]} fromSelf />
  113. </ChatProviders>
  114. )
  115. screen.getByText('this message has been edited')
  116. screen.getByText('(edited)')
  117. })
  118. it('does not render "(edited)" indicator for non-edited message', function () {
  119. const message: MessageType = {
  120. content: 'this message was not edited',
  121. user: currentUser,
  122. id: 'msg_1',
  123. timestamp: new Date('2025-01-01T00:00:00.000Z').getTime(),
  124. edited: false,
  125. }
  126. render(
  127. <ChatProviders>
  128. <MessageGroup messages={[message]} fromSelf />
  129. </ChatProviders>
  130. )
  131. screen.getByText('this message was not edited')
  132. expect(screen.queryByText('(edited)')).to.not.exist
  133. })
  134. it('renders message being edited with textarea and action buttons', function () {
  135. const messageBeingEdited: MessageType = {
  136. content: 'original message content',
  137. user: currentUser,
  138. id: 'msg_being_edited',
  139. timestamp: new Date('2025-01-01T00:00:00.000Z').getTime(),
  140. }
  141. render(
  142. <ChatProviders idOfMessageBeingEdited="msg_being_edited">
  143. <MessageGroup messages={[messageBeingEdited]} fromSelf />
  144. </ChatProviders>
  145. )
  146. const textarea = screen.getByDisplayValue('original message content')
  147. expect(textarea.tagName.toLowerCase()).to.equal('textarea')
  148. screen.getByRole('button', { name: 'Cancel' })
  149. screen.getByRole('button', { name: 'Save' })
  150. const paragraphs = screen.queryAllByText('original message content', {
  151. selector: 'p',
  152. })
  153. expect(paragraphs).to.have.length(0)
  154. })
  155. describe('when the message is from the user themselves', function () {
  156. const message: MessageType = {
  157. content: 'a message',
  158. user: currentUser,
  159. id: 'msg_1',
  160. timestamp: new Date('2025-01-01T00:00:00.000Z').getTime(),
  161. }
  162. it('does not render the user name nor the email', function () {
  163. render(
  164. <ChatProviders>
  165. <MessageGroup messages={[message]} fromSelf />
  166. </ChatProviders>
  167. )
  168. expect(screen.queryByText(currentUser.first_name!)).to.not.exist
  169. expect(screen.queryByText(currentUser.email)).to.not.exist
  170. })
  171. })
  172. describe('when the message is from other user', function () {
  173. const otherUser: User = {
  174. id: 'other_user' as UserId,
  175. first_name: 'other_user_first_name',
  176. email: 'other@example.com',
  177. }
  178. const message: MessageType = {
  179. content: 'a message',
  180. user: otherUser,
  181. id: 'msg_1',
  182. timestamp: new Date('2025-01-01T00:00:00.000Z').getTime(),
  183. }
  184. it('should render the other user name', function () {
  185. render(
  186. <ChatProviders>
  187. <MessageGroup
  188. messages={[message]}
  189. user={otherUser}
  190. fromSelf={false}
  191. />
  192. </ChatProviders>
  193. )
  194. screen.getByText(otherUser.first_name!)
  195. })
  196. it('should render the other user email when their name is not available', function () {
  197. const msg: MessageType = {
  198. content: message.content,
  199. user: {
  200. id: otherUser.id,
  201. email: 'other@example.com',
  202. },
  203. id: 'msg_1',
  204. timestamp: new Date('2025-01-01T00:00:00.000Z').getTime(),
  205. }
  206. render(
  207. <ChatProviders>
  208. <MessageGroup messages={[msg]} user={msg.user} fromSelf={false} />
  209. </ChatProviders>
  210. )
  211. expect(screen.queryByText(otherUser.first_name!)).to.not.exist
  212. screen.getByText(msg.user!.email)
  213. })
  214. })
  215. })