index.spec.tsx 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import {GroupsFixture} from 'sentry-fixture/groups';
  2. import {OrganizationFixture} from 'sentry-fixture/organization';
  3. import {ProjectFixture} from 'sentry-fixture/project';
  4. import {RouterContextFixture} from 'sentry-fixture/routerContextFixture';
  5. import {RouterFixture} from 'sentry-fixture/routerFixture';
  6. import {
  7. render,
  8. renderGlobalModal,
  9. screen,
  10. userEvent,
  11. waitFor,
  12. } from 'sentry-test/reactTestingLibrary';
  13. import GroupSimilarIssues from 'sentry/views/issueDetails/groupSimilarIssues';
  14. const MockNavigate = jest.fn();
  15. jest.mock('sentry/utils/useNavigate', () => ({
  16. useNavigate: () => MockNavigate,
  17. }));
  18. describe('Issues Similar View', function () {
  19. let mock;
  20. const project = ProjectFixture({
  21. features: ['similarity-view'],
  22. });
  23. const routerContext = RouterContextFixture([
  24. {
  25. router: {
  26. ...RouterFixture(),
  27. params: {orgId: 'org-slug', projectId: 'project-slug', groupId: 'group-id'},
  28. },
  29. },
  30. ]);
  31. const scores = [
  32. {'exception:stacktrace:pairs': 0.375},
  33. {'exception:stacktrace:pairs': 0.01264},
  34. {'exception:stacktrace:pairs': 0.875},
  35. {'exception:stacktrace:pairs': 0.001488},
  36. ];
  37. const mockData = {
  38. similar: GroupsFixture().map((issue, i) => [issue, scores[i]]),
  39. };
  40. const router = RouterFixture();
  41. beforeEach(function () {
  42. mock = MockApiClient.addMockResponse({
  43. url: '/organizations/org-slug/issues/group-id/similar/?limit=50',
  44. body: mockData.similar,
  45. });
  46. });
  47. afterEach(() => {
  48. MockApiClient.clearMockResponses();
  49. jest.clearAllMocks();
  50. });
  51. const selectNthSimilarItem = async (index: number) => {
  52. const items = await screen.findAllByTestId('similar-item-row');
  53. const item = items.at(index);
  54. expect(item).toBeDefined();
  55. await userEvent.click(item!);
  56. };
  57. it('renders with mocked data', async function () {
  58. render(
  59. <GroupSimilarIssues
  60. project={project}
  61. params={{orgId: 'org-slug', groupId: 'group-id'}}
  62. location={router.location}
  63. router={router}
  64. routeParams={router.params}
  65. routes={router.routes}
  66. route={{}}
  67. />,
  68. {context: routerContext}
  69. );
  70. expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
  71. await waitFor(() => expect(mock).toHaveBeenCalled());
  72. expect(screen.getByText('Show 3 issues below threshold')).toBeInTheDocument();
  73. });
  74. it('can merge and redirect to new parent', async function () {
  75. const merge = MockApiClient.addMockResponse({
  76. method: 'PUT',
  77. url: '/projects/org-slug/project-slug/issues/',
  78. body: {
  79. merge: {children: ['123'], parent: '321'},
  80. },
  81. });
  82. render(
  83. <GroupSimilarIssues
  84. project={project}
  85. params={{orgId: 'org-slug', groupId: 'group-id'}}
  86. location={router.location}
  87. router={router}
  88. routeParams={router.params}
  89. routes={router.routes}
  90. route={{}}
  91. />,
  92. {context: routerContext}
  93. );
  94. renderGlobalModal();
  95. await selectNthSimilarItem(0);
  96. await userEvent.click(await screen.findByRole('button', {name: 'Merge (1)'}));
  97. await userEvent.click(screen.getByRole('button', {name: 'Confirm'}));
  98. await waitFor(() => {
  99. expect(merge).toHaveBeenCalledWith(
  100. '/projects/org-slug/project-slug/issues/',
  101. expect.objectContaining({
  102. data: {merge: 1},
  103. })
  104. );
  105. });
  106. expect(MockNavigate).toHaveBeenCalledWith(
  107. '/organizations/org-slug/issues/321/similar/'
  108. );
  109. });
  110. it('correctly shows merge count', async function () {
  111. render(
  112. <GroupSimilarIssues
  113. project={project}
  114. params={{orgId: 'org-slug', groupId: 'group-id'}}
  115. location={router.location}
  116. router={router}
  117. routeParams={router.params}
  118. routes={router.routes}
  119. route={{}}
  120. />,
  121. {context: routerContext}
  122. );
  123. renderGlobalModal();
  124. await userEvent.click(await screen.findByTestId('similar-item-row'));
  125. expect(screen.getByText('Merge (1)')).toBeInTheDocument();
  126. // Correctly show "Merge (0)" when the item is un-clicked
  127. await userEvent.click(await screen.findByTestId('similar-item-row'));
  128. expect(screen.getByText('Merge (0)')).toBeInTheDocument();
  129. });
  130. it('renders all filtered issues with issues-similarity-embeddings flag', async function () {
  131. const features = ['issues-similarity-embeddings'];
  132. render(
  133. <GroupSimilarIssues
  134. project={project}
  135. params={{orgId: 'org-slug', groupId: 'group-id'}}
  136. location={router.location}
  137. router={router}
  138. routeParams={router.params}
  139. routes={router.routes}
  140. route={{}}
  141. />,
  142. {context: routerContext, organization: OrganizationFixture({features})}
  143. );
  144. expect(screen.getByTestId('loading-indicator')).toBeInTheDocument();
  145. await waitFor(() => expect(mock).toHaveBeenCalled());
  146. expect(screen.queryByText('Show 3 issues below threshold')).not.toBeInTheDocument();
  147. });
  148. });