index.spec.tsx 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. import {GroupFixture} from 'sentry-fixture/group';
  2. import {OrganizationFixture} from 'sentry-fixture/organization';
  3. import {RouterFixture} from 'sentry-fixture/routerFixture';
  4. import {render, screen} from 'sentry-test/reactTestingLibrary';
  5. import {GroupRelatedIssues} from 'sentry/views/issueDetails/groupRelatedIssues';
  6. describe('Related Issues View', function () {
  7. const organization = OrganizationFixture({features: ['global-views']});
  8. const groupId = '12345678';
  9. const group = GroupFixture({id: groupId});
  10. const router = RouterFixture({
  11. params: {groupId: group.id},
  12. });
  13. const orgSlug = organization.slug;
  14. const group1 = '15';
  15. const group2 = '20';
  16. // query=issue.id:[15,20] -> query=issue.id%3A%5B15%2C20%5D
  17. const orgIssuesEndpoint = `/organizations/${orgSlug}/issues/?query=issue.id%3A%5B${group1}%2C${group2}%5D`;
  18. const errorType = 'RuntimeError';
  19. const onlySameRootData = {
  20. type: 'same_root_cause',
  21. data: [group1, group2],
  22. };
  23. const onlyTraceConnectedData = {
  24. type: 'trace_connected',
  25. data: [group1, group2],
  26. meta: {
  27. event_id: 'abcd',
  28. trace_id: '1234',
  29. },
  30. };
  31. const issuesData = [
  32. {
  33. id: group1,
  34. shortId: `EARTH-${group1}`,
  35. project: {id: '3', name: 'Earth', slug: 'earth', platform: null},
  36. type: 'error',
  37. metadata: {
  38. type: errorType,
  39. },
  40. issueCategory: 'error',
  41. lastSeen: '2024-03-15T20:15:30Z',
  42. },
  43. {
  44. id: group2,
  45. shortId: `EARTH-${group2}`,
  46. project: {id: '3', name: 'Earth', slug: 'earth', platform: null},
  47. type: 'error',
  48. metadata: {
  49. type: errorType,
  50. },
  51. issueCategory: 'error',
  52. lastSeen: '2024-03-16T20:15:30Z',
  53. },
  54. ];
  55. beforeEach(function () {
  56. // GroupList calls this but we don't need it for this test
  57. MockApiClient.addMockResponse({
  58. url: `/organizations/${orgSlug}/users/`,
  59. body: {},
  60. });
  61. MockApiClient.addMockResponse({
  62. url: `/organizations/org-slug/issues/${group.id}/`,
  63. body: group,
  64. });
  65. });
  66. afterEach(() => {
  67. MockApiClient.clearMockResponses();
  68. jest.clearAllMocks();
  69. });
  70. it('renders with same root issues', async function () {
  71. const sameRootIssuesMock = MockApiClient.addMockResponse({
  72. url: `/issues/${groupId}/related-issues/`,
  73. match: [
  74. MockApiClient.matchQuery({
  75. type: 'same_root_cause',
  76. }),
  77. ],
  78. body: onlySameRootData,
  79. });
  80. MockApiClient.addMockResponse({
  81. url: `/issues/${groupId}/related-issues/`,
  82. match: [
  83. MockApiClient.matchQuery({
  84. type: 'trace_connected',
  85. }),
  86. ],
  87. body: [],
  88. });
  89. const issuesMock = MockApiClient.addMockResponse({
  90. url: orgIssuesEndpoint,
  91. body: issuesData,
  92. });
  93. render(<GroupRelatedIssues group={group} />, {router, organization});
  94. // Wait for the issues showing up on the table
  95. expect(await screen.findByText(`EARTH-${group1}`)).toBeInTheDocument();
  96. expect(await screen.findByText(`EARTH-${group2}`)).toBeInTheDocument();
  97. expect(sameRootIssuesMock).toHaveBeenCalled();
  98. expect(issuesMock).toHaveBeenCalled();
  99. const linkButton = screen.getByRole('button', {name: /open in issues/i});
  100. expect(linkButton).toHaveAttribute(
  101. 'href',
  102. // Opening in Issues needs to include the group we are currently viewing
  103. `/organizations/org-slug/issues/?project=-1&query=${encodeURIComponent(`issue.id:[${groupId},${group1},${group2}]`)}`
  104. );
  105. });
  106. it('renders with trace connected issues', async function () {
  107. MockApiClient.addMockResponse({
  108. url: `/issues/${groupId}/related-issues/`,
  109. match: [
  110. MockApiClient.matchQuery({
  111. type: 'same_root_cause',
  112. }),
  113. ],
  114. body: [],
  115. });
  116. const traceIssuesMock = MockApiClient.addMockResponse({
  117. url: `/issues/${groupId}/related-issues/`,
  118. match: [
  119. MockApiClient.matchQuery({
  120. type: 'trace_connected',
  121. }),
  122. ],
  123. body: onlyTraceConnectedData,
  124. });
  125. const issuesMock = MockApiClient.addMockResponse({
  126. url: orgIssuesEndpoint,
  127. body: issuesData,
  128. });
  129. render(<GroupRelatedIssues group={group} />, {router, organization});
  130. // Wait for the issues showing up on the table
  131. expect(await screen.findByText(`EARTH-${group1}`)).toBeInTheDocument();
  132. expect(await screen.findByText(`EARTH-${group2}`)).toBeInTheDocument();
  133. expect(traceIssuesMock).toHaveBeenCalled();
  134. expect(issuesMock).toHaveBeenCalled();
  135. const linkElement = screen.getByRole('link', {name: /this trace/i});
  136. expect(linkElement).toHaveAttribute(
  137. 'href',
  138. '/organizations/org-slug/performance/trace/1234/?node=error-abcd'
  139. );
  140. const linkButton = screen.getByRole('button', {name: /open in issues/i});
  141. // The Issue search supports using `trace` as a parameter
  142. expect(linkButton).toHaveAttribute(
  143. 'href',
  144. `/organizations/org-slug/issues/?project=-1&query=${encodeURIComponent('trace:1234')}`
  145. );
  146. });
  147. it('sets project id when global views is disabled', async function () {
  148. MockApiClient.addMockResponse({
  149. url: `/issues/${groupId}/related-issues/`,
  150. match: [
  151. MockApiClient.matchQuery({
  152. type: 'same_root_cause',
  153. project: group.project.id,
  154. }),
  155. ],
  156. body: [],
  157. });
  158. MockApiClient.addMockResponse({
  159. url: `/issues/${groupId}/related-issues/`,
  160. match: [
  161. MockApiClient.matchQuery({
  162. type: 'trace_connected',
  163. project: group.project.id,
  164. }),
  165. ],
  166. body: onlyTraceConnectedData,
  167. });
  168. MockApiClient.addMockResponse({
  169. url: `/organizations/${orgSlug}/issues/?project=${group.project.id}&query=${encodeURIComponent(`issue.id:[${group1},${group2}]`)}`,
  170. body: issuesData,
  171. });
  172. const noGlobalViewsOrganization = OrganizationFixture({features: []});
  173. render(<GroupRelatedIssues group={group} />, {
  174. router,
  175. organization: noGlobalViewsOrganization,
  176. });
  177. expect(await screen.findByText(`EARTH-${group1}`)).toBeInTheDocument();
  178. const linkButton = screen.getByRole('button', {name: /open in issues/i});
  179. expect(linkButton).toHaveAttribute(
  180. 'href',
  181. `/organizations/org-slug/issues/?project=${group.project.id}&query=${encodeURIComponent('trace:1234')}`
  182. );
  183. });
  184. });