solutionsHubDrawer.spec.tsx 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. import {AutofixDataFixture} from 'sentry-fixture/autofixData';
  2. import {AutofixStepFixture} from 'sentry-fixture/autofixStep';
  3. import {EventFixture} from 'sentry-fixture/event';
  4. import {FrameFixture} from 'sentry-fixture/frame';
  5. import {GroupFixture} from 'sentry-fixture/group';
  6. import {OrganizationFixture} from 'sentry-fixture/organization';
  7. import {ProjectFixture} from 'sentry-fixture/project';
  8. import {
  9. render,
  10. screen,
  11. userEvent,
  12. waitFor,
  13. waitForElementToBeRemoved,
  14. } from 'sentry-test/reactTestingLibrary';
  15. import {t} from 'sentry/locale';
  16. import {EntryType} from 'sentry/types/event';
  17. import {SolutionsHubDrawer} from 'sentry/views/issueDetails/streamline/solutionsHubDrawer';
  18. describe('SolutionsHubDrawer', () => {
  19. const organization = OrganizationFixture({
  20. genAIConsent: true,
  21. hideAiFeatures: false,
  22. features: ['gen-ai-features'],
  23. });
  24. const mockEvent = EventFixture({
  25. entries: [
  26. {
  27. type: EntryType.EXCEPTION,
  28. data: {values: [{stacktrace: {frames: [FrameFixture()]}}]},
  29. },
  30. ],
  31. });
  32. const mockGroup = GroupFixture();
  33. const mockProject = ProjectFixture();
  34. const mockAutofixData = AutofixDataFixture({steps: [AutofixStepFixture()]});
  35. beforeEach(() => {
  36. MockApiClient.clearMockResponses();
  37. MockApiClient.addMockResponse({
  38. url: `/issues/${mockGroup.id}/autofix/setup/`,
  39. body: {
  40. genAIConsent: {ok: true},
  41. integration: {ok: true},
  42. githubWriteIntegration: {ok: true},
  43. },
  44. });
  45. MockApiClient.addMockResponse({
  46. url: `/organizations/${mockProject.organization.slug}/issues/${mockGroup.id}/summarize/`,
  47. method: 'POST',
  48. body: {
  49. whatsWrong: 'Test whats wrong',
  50. trace: 'Test trace',
  51. possibleCause: 'Test possible cause',
  52. headline: 'Test headline',
  53. },
  54. });
  55. });
  56. it('renders consent state if not consented', async () => {
  57. MockApiClient.addMockResponse({
  58. url: `/issues/${mockGroup.id}/autofix/setup/`,
  59. body: {
  60. genAIConsent: {ok: false},
  61. integration: {ok: false},
  62. githubWriteIntegration: {ok: false},
  63. },
  64. });
  65. MockApiClient.addMockResponse({
  66. url: `/issues/${mockGroup.id}/autofix/`,
  67. body: {autofix: null},
  68. });
  69. render(
  70. <SolutionsHubDrawer event={mockEvent} group={mockGroup} project={mockProject} />,
  71. {organization}
  72. );
  73. expect(screen.getByTestId('ai-setup-loading-indicator')).toBeInTheDocument();
  74. await waitForElementToBeRemoved(() =>
  75. screen.queryByTestId('ai-setup-loading-indicator')
  76. );
  77. expect(screen.getByText(mockEvent.id)).toBeInTheDocument();
  78. expect(screen.getByRole('heading', {name: 'Solutions Hub'})).toBeInTheDocument();
  79. expect(screen.getByTestId('ai-setup-data-consent')).toBeInTheDocument();
  80. });
  81. it('renders initial state correctly', async () => {
  82. MockApiClient.addMockResponse({
  83. url: `/issues/${mockGroup.id}/autofix/`,
  84. body: {autofix: null},
  85. });
  86. render(
  87. <SolutionsHubDrawer event={mockEvent} group={mockGroup} project={mockProject} />,
  88. {organization}
  89. );
  90. expect(screen.getByTestId('ai-setup-loading-indicator')).toBeInTheDocument();
  91. await waitForElementToBeRemoved(() =>
  92. screen.queryByTestId('ai-setup-loading-indicator')
  93. );
  94. expect(screen.getByRole('heading', {name: 'Solutions Hub'})).toBeInTheDocument();
  95. const startButton = screen.getByRole('button', {name: 'Start Autofix'});
  96. expect(startButton).toBeInTheDocument();
  97. });
  98. it('triggers autofix on clicking the Start button', async () => {
  99. MockApiClient.addMockResponse({
  100. url: `/issues/${mockGroup.id}/autofix/`,
  101. method: 'POST',
  102. body: {autofix: null},
  103. });
  104. MockApiClient.addMockResponse({
  105. url: `/issues/${mockGroup.id}/autofix/`,
  106. method: 'GET',
  107. body: {autofix: null},
  108. });
  109. render(
  110. <SolutionsHubDrawer event={mockEvent} group={mockGroup} project={mockProject} />,
  111. {organization}
  112. );
  113. expect(screen.getByTestId('ai-setup-loading-indicator')).toBeInTheDocument();
  114. await waitForElementToBeRemoved(() =>
  115. screen.queryByTestId('ai-setup-loading-indicator')
  116. );
  117. const startButton = screen.getByRole('button', {name: 'Start Autofix'});
  118. await userEvent.click(startButton);
  119. expect(
  120. await screen.findByRole('button', {name: t('Start Over')})
  121. ).toBeInTheDocument();
  122. });
  123. it('displays autofix steps and Start Over button when autofixData is available', async () => {
  124. MockApiClient.addMockResponse({
  125. url: `/issues/${mockGroup.id}/autofix/`,
  126. body: {autofix: mockAutofixData},
  127. });
  128. render(
  129. <SolutionsHubDrawer event={mockEvent} group={mockGroup} project={mockProject} />,
  130. {organization}
  131. );
  132. expect(
  133. await screen.findByRole('button', {name: t('Start Over')})
  134. ).toBeInTheDocument();
  135. });
  136. it('resets autofix on clicking the start over button', async () => {
  137. MockApiClient.addMockResponse({
  138. url: `/issues/${mockGroup.id}/autofix/`,
  139. body: {autofix: mockAutofixData},
  140. });
  141. render(
  142. <SolutionsHubDrawer event={mockEvent} group={mockGroup} project={mockProject} />,
  143. {organization}
  144. );
  145. const startOverButton = await screen.findByRole('button', {name: t('Start Over')});
  146. expect(startOverButton).toBeInTheDocument();
  147. await userEvent.click(startOverButton);
  148. await waitFor(() => {
  149. expect(screen.getByRole('button', {name: 'Start Autofix'})).toBeInTheDocument();
  150. });
  151. });
  152. it('shows setup if not complete', async () => {
  153. MockApiClient.addMockResponse({
  154. url: `/issues/${mockGroup.id}/autofix/setup/`,
  155. body: {
  156. genAIConsent: {ok: true},
  157. integration: {ok: false},
  158. githubWriteIntegration: {ok: false, repos: []},
  159. },
  160. });
  161. MockApiClient.addMockResponse({
  162. url: `/issues/${mockGroup.id}/autofix/`,
  163. body: {autofix: null},
  164. });
  165. MockApiClient.addMockResponse({
  166. url: '/organizations/org-slug/integrations/?provider_key=github&includeConfig=0',
  167. body: [],
  168. });
  169. render(
  170. <SolutionsHubDrawer event={mockEvent} group={mockGroup} project={mockProject} />,
  171. {organization}
  172. );
  173. expect(screen.getByTestId('ai-setup-loading-indicator')).toBeInTheDocument();
  174. await waitForElementToBeRemoved(() =>
  175. screen.queryByTestId('ai-setup-loading-indicator')
  176. );
  177. expect(screen.getByRole('heading', {name: 'Solutions Hub'})).toBeInTheDocument();
  178. expect(screen.queryByRole('button', {name: 'Start Autofix'})).not.toBeInTheDocument();
  179. expect(await screen.findByText('Install the GitHub Integration')).toBeInTheDocument();
  180. });
  181. });