groupActivity.spec.jsx 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  1. import {initializeOrg} from 'sentry-test/initializeOrg';
  2. import {
  3. act,
  4. render,
  5. renderGlobalModal,
  6. screen,
  7. userEvent,
  8. waitFor,
  9. } from 'sentry-test/reactTestingLibrary';
  10. import PullRequestLink from 'sentry/components/pullRequestLink';
  11. import ConfigStore from 'sentry/stores/configStore';
  12. import GroupStore from 'sentry/stores/groupStore';
  13. import OrganizationStore from 'sentry/stores/organizationStore';
  14. import ProjectsStore from 'sentry/stores/projectsStore';
  15. import TeamStore from 'sentry/stores/teamStore';
  16. import {GroupActivity} from 'sentry/views/issueDetails/groupActivity';
  17. describe('GroupActivity', function () {
  18. let project;
  19. beforeEach(function () {
  20. project = TestStubs.Project();
  21. ProjectsStore.loadInitialData([project]);
  22. ConfigStore.init();
  23. ConfigStore.set('user', {id: '123'});
  24. GroupStore.init();
  25. });
  26. afterEach(() => {
  27. MockApiClient.clearMockResponses();
  28. jest.clearAllMocks();
  29. });
  30. function createWrapper({activity, organization: additionalOrg} = {}) {
  31. const group = TestStubs.Group({
  32. id: '1337',
  33. activity: activity ?? [
  34. {type: 'note', id: 'note-1', data: {text: 'Test Note'}, user: TestStubs.User()},
  35. ],
  36. project,
  37. });
  38. const {organization, routerContext} = initializeOrg({
  39. organization: additionalOrg,
  40. group,
  41. });
  42. GroupStore.add([group]);
  43. TeamStore.loadInitialData([TestStubs.Team({id: '999', slug: 'no-team'})]);
  44. OrganizationStore.onUpdate(organization, {replace: true});
  45. return render(
  46. <GroupActivity
  47. api={new MockApiClient()}
  48. params={{orgId: 'org-slug'}}
  49. group={group}
  50. organization={organization}
  51. />,
  52. {context: routerContext}
  53. );
  54. }
  55. it('renders a NoteInput', function () {
  56. createWrapper();
  57. expect(screen.getByTestId('activity-note-body')).toBeInTheDocument();
  58. });
  59. it('renders a marked reviewed activity', function () {
  60. const user = TestStubs.User({name: 'Samwise'});
  61. createWrapper({
  62. activity: [{type: 'mark_reviewed', id: 'reviewed-1', data: {}, user}],
  63. });
  64. expect(screen.getByText('marked this issue as reviewed')).toBeInTheDocument();
  65. expect(screen.getByText(user.name)).toBeInTheDocument();
  66. });
  67. it('renders a pr activity', function () {
  68. const user = TestStubs.User({name: 'Test User'});
  69. const repository = TestStubs.Repository();
  70. const pullRequest = TestStubs.PullRequest({message: 'Fixes ISSUE-1'});
  71. createWrapper({
  72. activity: [
  73. {
  74. type: 'set_resolved_in_pull_request',
  75. id: 'pr-1',
  76. data: {
  77. pullRequest: {
  78. author: 'Test User',
  79. version: (
  80. <PullRequestLink
  81. inline
  82. pullRequest={pullRequest}
  83. repository={pullRequest.repository}
  84. />
  85. ),
  86. repository: {repository},
  87. },
  88. },
  89. user,
  90. },
  91. ],
  92. });
  93. expect(screen.getAllByTestId('activity-item').at(-1)).toHaveTextContent(
  94. 'Test User has created a PR for this issue:'
  95. );
  96. });
  97. it('renders a assigned to self activity', function () {
  98. const user = TestStubs.User({id: '301', name: 'Mark'});
  99. createWrapper({
  100. activity: [
  101. {
  102. data: {
  103. assignee: user.id,
  104. assigneeEmail: user.email,
  105. assigneeType: 'user',
  106. },
  107. dateCreated: '2021-10-01T15:31:38.950115Z',
  108. id: '117',
  109. type: 'assigned',
  110. user,
  111. },
  112. ],
  113. });
  114. expect(screen.getAllByTestId('activity-item').at(-1)).toHaveTextContent(
  115. /Mark assigned this issue to themselves/
  116. );
  117. });
  118. it('renders an assigned via codeowners activity', function () {
  119. createWrapper({
  120. activity: [
  121. {
  122. data: {
  123. assignee: '123',
  124. assigneeEmail: 'anotheruser@sentry.io',
  125. assigneeType: 'user',
  126. integration: 'codeowners',
  127. rule: 'path:something/*.py #workflow',
  128. },
  129. dateCreated: '2021-10-01T15:31:38.950115Z',
  130. id: '117',
  131. type: 'assigned',
  132. user: null,
  133. },
  134. ],
  135. });
  136. expect(screen.getAllByTestId('activity-item').at(-1)).toHaveTextContent(
  137. /Sentry auto-assigned this issue to anotheruser@sentry.io/
  138. );
  139. });
  140. it('renders an assigned via slack activity', function () {
  141. const user = TestStubs.User({id: '301', name: 'Mark'});
  142. createWrapper({
  143. activity: [
  144. {
  145. data: {
  146. assignee: '123',
  147. assigneeEmail: 'anotheruser@sentry.io',
  148. assigneeType: 'user',
  149. integration: 'slack',
  150. },
  151. dateCreated: '2021-10-01T15:31:38.950115Z',
  152. id: '117',
  153. type: 'assigned',
  154. user,
  155. },
  156. ],
  157. });
  158. const item = screen.getAllByTestId('activity-item').at(-1);
  159. expect(item).toHaveTextContent(/Mark assigned this issue to anotheruser@sentry.io/);
  160. expect(item).toHaveTextContent(/Assigned via Slack/);
  161. });
  162. it('resolved in commit with no releases', function () {
  163. createWrapper({
  164. activity: [
  165. {
  166. type: 'set_resolved_in_commit',
  167. id: '123',
  168. data: {
  169. author: 'hello',
  170. commit: {
  171. id: 'komal-commit',
  172. repository: {},
  173. releases: [],
  174. },
  175. },
  176. user: TestStubs.User(),
  177. },
  178. ],
  179. });
  180. expect(screen.getAllByTestId('activity-item').at(-1)).toHaveTextContent(
  181. 'Foo Bar marked this issue as resolved in komal-commit'
  182. );
  183. });
  184. it('resolved in commit with one release', function () {
  185. createWrapper({
  186. activity: [
  187. {
  188. type: 'set_resolved_in_commit',
  189. id: '123',
  190. data: {
  191. author: 'hello',
  192. commit: {
  193. id: 'komal-commit',
  194. repository: {},
  195. releases: [
  196. {
  197. dateCreated: '2022-05-01',
  198. dateReleased: '2022-05-02',
  199. version: 'random',
  200. },
  201. ],
  202. },
  203. },
  204. user: TestStubs.User(),
  205. },
  206. ],
  207. });
  208. expect(screen.getAllByTestId('activity-item').at(-1)).toHaveTextContent(
  209. 'Foo Bar marked this issue as resolved in komal-commit This commit was released in random'
  210. );
  211. });
  212. it('resolved in commit with multiple releases', function () {
  213. createWrapper({
  214. activity: [
  215. {
  216. type: 'set_resolved_in_commit',
  217. id: '123',
  218. data: {
  219. commit: {
  220. id: 'komal-commit',
  221. repository: {},
  222. releases: [
  223. {
  224. dateCreated: '2022-05-01',
  225. dateReleased: '2022-05-02',
  226. version: 'random',
  227. },
  228. {
  229. dateCreated: '2022-06-01',
  230. dateReleased: '2022-06-02',
  231. version: 'newest',
  232. },
  233. {
  234. dateCreated: '2021-08-03',
  235. dateReleased: '2021-08-03',
  236. version: 'oldest-release',
  237. },
  238. {
  239. dateCreated: '2022-04-21',
  240. dateReleased: '2022-04-21',
  241. version: 'randomTwo',
  242. },
  243. ],
  244. },
  245. },
  246. user: TestStubs.User(),
  247. },
  248. ],
  249. });
  250. expect(screen.getAllByTestId('activity-item').at(-1)).toHaveTextContent(
  251. 'Foo Bar marked this issue as resolved in komal-commit This commit was released in oldest-release and 3 others'
  252. );
  253. });
  254. it('requests assignees that are not in the team store', async function () {
  255. const team = TestStubs.Team({id: '123', name: 'workflow'});
  256. const teamRequest = MockApiClient.addMockResponse({
  257. url: `/organizations/org-slug/teams/`,
  258. body: [team],
  259. });
  260. createWrapper({
  261. activity: [
  262. {
  263. id: '123',
  264. user: null,
  265. type: 'assigned',
  266. data: {
  267. assignee: team.id,
  268. assigneeEmail: null,
  269. assigneeType: 'team',
  270. },
  271. dateCreated: '2021-10-28T13:40:10.634821Z',
  272. },
  273. ],
  274. });
  275. await waitFor(() => expect(teamRequest).toHaveBeenCalledTimes(1));
  276. expect(
  277. await screen.findByText(`assigned this issue to #${team.slug}`)
  278. ).toBeInTheDocument();
  279. expect(screen.getAllByTestId('activity-item').at(-1)).toHaveTextContent(
  280. /Sentry assigned this issue to #team-slug/
  281. );
  282. });
  283. describe('Delete', function () {
  284. let deleteMock;
  285. beforeEach(function () {
  286. deleteMock = MockApiClient.addMockResponse({
  287. url: '/issues/1337/comments/note-1/',
  288. method: 'DELETE',
  289. });
  290. ConfigStore.set('user', {id: '123', isSuperuser: true});
  291. });
  292. it('should do nothing if not present in GroupStore', async function () {
  293. createWrapper();
  294. renderGlobalModal();
  295. act(() => {
  296. // Remove note from group activity
  297. GroupStore.removeActivity('1337', 'note-1');
  298. });
  299. await userEvent.click(screen.getByText('Remove'));
  300. expect(
  301. screen.getByText('Are you sure you wish to delete this comment?')
  302. ).toBeInTheDocument();
  303. await userEvent.click(screen.getByRole('button', {name: 'Confirm'}));
  304. expect(deleteMock).not.toHaveBeenCalled();
  305. });
  306. it('should remove remove the item from the GroupStore make a DELETE API request', async function () {
  307. createWrapper();
  308. renderGlobalModal();
  309. await userEvent.click(screen.getByText('Remove'));
  310. expect(
  311. screen.getByText('Are you sure you wish to delete this comment?')
  312. ).toBeInTheDocument();
  313. await userEvent.click(screen.getByRole('button', {name: 'Confirm'}));
  314. expect(deleteMock).toHaveBeenCalledTimes(1);
  315. });
  316. });
  317. });