solutionsHubDrawer.spec.tsx 6.5 KB

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