activitySection.spec.tsx 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. import {GroupFixture} from 'sentry-fixture/group';
  2. import {ProjectFixture} from 'sentry-fixture/project';
  3. import {UserFixture} from 'sentry-fixture/user';
  4. import {
  5. render,
  6. renderGlobalModal,
  7. screen,
  8. userEvent,
  9. } from 'sentry-test/reactTestingLibrary';
  10. import ConfigStore from 'sentry/stores/configStore';
  11. import GroupStore from 'sentry/stores/groupStore';
  12. import ProjectsStore from 'sentry/stores/projectsStore';
  13. import type {GroupActivity} from 'sentry/types/group';
  14. import {GroupActivityType} from 'sentry/types/group';
  15. import StreamlinedActivitySection from 'sentry/views/issueDetails/streamline/sidebar/activitySection';
  16. describe('StreamlinedActivitySection', function () {
  17. const project = ProjectFixture();
  18. const user = UserFixture();
  19. user.options.prefersIssueDetailsStreamlinedUI = true;
  20. ConfigStore.set('user', user);
  21. ProjectsStore.loadInitialData([project]);
  22. GroupStore.init();
  23. const group = GroupFixture({
  24. id: '1337',
  25. activity: [
  26. {
  27. type: GroupActivityType.NOTE,
  28. id: 'note-1',
  29. data: {text: 'Test Note'},
  30. dateCreated: '2020-01-01T00:00:00',
  31. user: user,
  32. project,
  33. },
  34. ],
  35. project,
  36. });
  37. GroupStore.add([group]);
  38. beforeEach(() => {
  39. jest.restoreAllMocks();
  40. MockApiClient.clearMockResponses();
  41. });
  42. it('renders the input with a comment button', async function () {
  43. const comment = 'nice work friends';
  44. const postMock = MockApiClient.addMockResponse({
  45. url: '/organizations/org-slug/issues/1337/comments/',
  46. method: 'POST',
  47. body: {
  48. id: 'note-2',
  49. user: UserFixture({id: '2'}),
  50. type: 'note',
  51. data: {text: comment},
  52. dateCreated: '2024-10-31T00:00:00.000000Z',
  53. },
  54. });
  55. render(<StreamlinedActivitySection group={group} />);
  56. const commentInput = screen.getByRole('textbox', {name: 'Add a comment'});
  57. expect(commentInput).toBeInTheDocument();
  58. expect(
  59. screen.queryByRole('button', {name: 'Submit comment'})
  60. ).not.toBeInTheDocument();
  61. await userEvent.click(commentInput);
  62. // Button appears after input is focused
  63. const submitButton = await screen.findByRole('button', {name: 'Submit comment'});
  64. expect(submitButton).toBeInTheDocument();
  65. expect(submitButton).toBeDisabled();
  66. await userEvent.type(commentInput, comment);
  67. expect(submitButton).toBeEnabled();
  68. await userEvent.click(submitButton);
  69. expect(postMock).toHaveBeenCalled();
  70. });
  71. it('allows submitting the comment field with hotkeys', async function () {
  72. const comment = 'nice work friends';
  73. const postMock = MockApiClient.addMockResponse({
  74. url: '/organizations/org-slug/issues/1337/comments/',
  75. method: 'POST',
  76. body: {
  77. id: 'note-3',
  78. user: UserFixture({id: '2'}),
  79. type: 'note',
  80. data: {text: comment},
  81. dateCreated: '2024-10-31T00:00:00.000000Z',
  82. },
  83. });
  84. render(<StreamlinedActivitySection group={group} />);
  85. const commentInput = screen.getByRole('textbox', {name: 'Add a comment'});
  86. await userEvent.type(commentInput, comment);
  87. await userEvent.keyboard('{Meta>}{Enter}{/Meta}');
  88. expect(postMock).toHaveBeenCalled();
  89. });
  90. it('renders note and allows for delete', async function () {
  91. const deleteMock = MockApiClient.addMockResponse({
  92. url: '/organizations/org-slug/issues/1337/comments/note-1/',
  93. method: 'DELETE',
  94. });
  95. render(<StreamlinedActivitySection group={group} />);
  96. renderGlobalModal();
  97. expect(await screen.findByText('Test Note')).toBeInTheDocument();
  98. expect(screen.getByRole('button', {name: 'Comment Actions'})).toBeInTheDocument();
  99. await userEvent.click(screen.getByRole('button', {name: 'Comment Actions'}));
  100. await userEvent.click(screen.getByRole('menuitemradio', {name: 'Remove'}));
  101. expect(
  102. screen.getByText('Are you sure you want to remove this comment?')
  103. ).toBeInTheDocument();
  104. await userEvent.click(screen.getByRole('button', {name: 'Remove comment'}));
  105. expect(deleteMock).toHaveBeenCalledTimes(1);
  106. expect(screen.queryByText('Test Note')).not.toBeInTheDocument();
  107. });
  108. it('renders note but does not allow for deletion if written by someone else', async function () {
  109. const updatedActivityGroup = GroupFixture({
  110. id: '1338',
  111. activity: [
  112. {
  113. type: GroupActivityType.NOTE,
  114. id: 'note-1',
  115. data: {text: 'Test Note'},
  116. dateCreated: '2020-01-01T00:00:00',
  117. user: UserFixture({id: '2'}),
  118. project,
  119. },
  120. ],
  121. project,
  122. });
  123. render(<StreamlinedActivitySection group={updatedActivityGroup} />);
  124. expect(await screen.findByText('Test Note')).toBeInTheDocument();
  125. expect(
  126. screen.queryByRole('button', {name: 'Comment Actions'})
  127. ).not.toBeInTheDocument();
  128. });
  129. it('collapses activity when there are more than 5 items', async function () {
  130. const activities: GroupActivity[] = Array.from({length: 7}, (_, index) => ({
  131. type: GroupActivityType.NOTE,
  132. id: `note-${index + 1}`,
  133. data: {text: `Test Note ${index + 1}`},
  134. dateCreated: '2020-01-01T00:00:00',
  135. user: UserFixture({id: '2'}),
  136. project,
  137. }));
  138. const updatedActivityGroup = GroupFixture({
  139. id: '1338',
  140. activity: activities,
  141. project,
  142. });
  143. render(<StreamlinedActivitySection group={updatedActivityGroup} />);
  144. expect(await screen.findByText('Test Note 1')).toBeInTheDocument();
  145. expect(await screen.findByText('Test Note 3')).toBeInTheDocument();
  146. expect(screen.queryByText('Test Note 7')).not.toBeInTheDocument();
  147. expect(await screen.findByText('View 4 more')).toBeInTheDocument();
  148. });
  149. it('does not collapse activity when rendered in the drawer', function () {
  150. const activities: GroupActivity[] = Array.from({length: 7}, (_, index) => ({
  151. type: GroupActivityType.NOTE,
  152. id: `note-${index + 1}`,
  153. data: {text: `Test Note ${index + 1}`},
  154. dateCreated: '2020-01-01T00:00:00',
  155. user: UserFixture({id: '2'}),
  156. project,
  157. }));
  158. const updatedActivityGroup = GroupFixture({
  159. id: '1338',
  160. activity: activities,
  161. project,
  162. });
  163. render(<StreamlinedActivitySection group={updatedActivityGroup} isDrawer />);
  164. for (const activity of activities) {
  165. expect(
  166. screen.getByText((activity.data as {text: string}).text)
  167. ).toBeInTheDocument();
  168. }
  169. expect(screen.queryByRole('button')).not.toBeInTheDocument();
  170. });
  171. });