solutionsSection.spec.tsx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  1. import {EventFixture} from 'sentry-fixture/event';
  2. import {GroupFixture} from 'sentry-fixture/group';
  3. import {OrganizationFixture} from 'sentry-fixture/organization';
  4. import {ProjectFixture} from 'sentry-fixture/project';
  5. import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
  6. import {getConfigForIssueType} from 'sentry/utils/issueTypeConfig';
  7. import SolutionsSection from 'sentry/views/issueDetails/streamline/solutionsSection';
  8. jest.mock('sentry/utils/issueTypeConfig');
  9. describe('SolutionsSection', () => {
  10. const mockEvent = EventFixture();
  11. const mockGroup = GroupFixture();
  12. const mockProject = ProjectFixture();
  13. const organization = OrganizationFixture({genAIConsent: true, hideAiFeatures: false});
  14. beforeEach(() => {
  15. MockApiClient.clearMockResponses();
  16. MockApiClient.addMockResponse({
  17. url: `/issues/${mockGroup.id}/autofix/setup/`,
  18. body: {
  19. genAIConsent: {ok: true},
  20. integration: {ok: true},
  21. githubWriteIntegration: {ok: true},
  22. },
  23. });
  24. jest.mocked(getConfigForIssueType).mockReturnValue({
  25. issueSummary: {
  26. enabled: true,
  27. },
  28. resources: {
  29. description: 'Test Resource',
  30. links: [{link: 'https://example.com', text: 'Test Link'}],
  31. linksByPlatform: {},
  32. },
  33. actions: {
  34. archiveUntilOccurrence: {enabled: false},
  35. delete: {enabled: false},
  36. deleteAndDiscard: {enabled: false},
  37. ignore: {enabled: false},
  38. merge: {enabled: false},
  39. resolveInRelease: {enabled: false},
  40. share: {enabled: false},
  41. },
  42. aiSuggestedSolution: false,
  43. attachments: {enabled: false},
  44. autofix: true,
  45. discover: {enabled: false},
  46. events: {enabled: false},
  47. evidence: null,
  48. filterAndSearchHeader: {enabled: false},
  49. mergedIssues: {enabled: false},
  50. performanceDurationRegression: {enabled: false},
  51. profilingDurationRegression: {enabled: false},
  52. regression: {enabled: false},
  53. replays: {enabled: false},
  54. showFeedbackWidget: false,
  55. similarIssues: {enabled: false},
  56. spanEvidence: {enabled: false},
  57. stacktrace: {enabled: false},
  58. stats: {enabled: false},
  59. tags: {enabled: false},
  60. tagsTab: {enabled: false},
  61. userFeedback: {enabled: false},
  62. usesIssuePlatform: false,
  63. });
  64. });
  65. it('renders loading state when summary is pending', () => {
  66. // Use a delayed response to simulate loading state
  67. MockApiClient.addMockResponse({
  68. url: `/organizations/${mockProject.organization.slug}/issues/${mockGroup.id}/summarize/`,
  69. method: 'POST',
  70. statusCode: 200,
  71. body: new Promise(() => {}), // Never resolves, keeping the loading state
  72. });
  73. render(
  74. <SolutionsSection event={mockEvent} group={mockGroup} project={mockProject} />,
  75. {
  76. organization,
  77. }
  78. );
  79. expect(screen.getByText('Solutions Hub')).toBeInTheDocument();
  80. expect(screen.getAllByTestId('loading-placeholder')).toHaveLength(3);
  81. });
  82. it('renders summary when AI features are enabled and data is available', async () => {
  83. const mockSummary = 'This is a test summary';
  84. MockApiClient.addMockResponse({
  85. url: `/organizations/${mockProject.organization.slug}/issues/${mockGroup.id}/summarize/`,
  86. method: 'POST',
  87. body: {
  88. whatsWrong: mockSummary,
  89. },
  90. });
  91. render(
  92. <SolutionsSection event={mockEvent} group={mockGroup} project={mockProject} />,
  93. {
  94. organization,
  95. }
  96. );
  97. await waitFor(() => {
  98. expect(screen.getByText(mockSummary)).toBeInTheDocument();
  99. expect(
  100. screen.getByRole('button', {name: 'Open Solutions Hub'})
  101. ).toBeInTheDocument();
  102. });
  103. });
  104. it('renders AI setup prompt when consent is not given', () => {
  105. const customOrganization = OrganizationFixture({
  106. genAIConsent: false,
  107. hideAiFeatures: false,
  108. });
  109. render(
  110. <SolutionsSection event={mockEvent} group={mockGroup} project={mockProject} />,
  111. {
  112. organization: customOrganization,
  113. }
  114. );
  115. expect(
  116. screen.getByText('Explore potential root causes and solutions with Sentry AI.')
  117. ).toBeInTheDocument();
  118. expect(screen.getByRole('button', {name: 'Open Solutions Hub'})).toBeInTheDocument();
  119. });
  120. it('renders resources section when AI features are disabled', () => {
  121. const customOrganization = OrganizationFixture({
  122. hideAiFeatures: true,
  123. genAIConsent: false,
  124. });
  125. render(
  126. <SolutionsSection event={mockEvent} group={mockGroup} project={mockProject} />,
  127. {
  128. organization: customOrganization,
  129. }
  130. );
  131. expect(screen.getByText('Test Link')).toBeInTheDocument();
  132. expect(screen.getByRole('button', {name: 'READ MORE'})).toBeInTheDocument();
  133. });
  134. it('toggles resources content when clicking Read More/Show Less', async () => {
  135. const customOrganization = OrganizationFixture({
  136. hideAiFeatures: true,
  137. genAIConsent: false,
  138. });
  139. render(
  140. <SolutionsSection event={mockEvent} group={mockGroup} project={mockProject} />,
  141. {
  142. organization: customOrganization,
  143. }
  144. );
  145. const readMoreButton = screen.getByRole('button', {name: 'READ MORE'});
  146. await userEvent.click(readMoreButton);
  147. expect(screen.getByRole('button', {name: 'SHOW LESS'})).toBeInTheDocument();
  148. const showLessButton = screen.getByRole('button', {name: 'SHOW LESS'});
  149. await userEvent.click(showLessButton);
  150. expect(screen.queryByRole('button', {name: 'SHOW LESS'})).not.toBeInTheDocument();
  151. expect(screen.getByRole('button', {name: 'READ MORE'})).toBeInTheDocument();
  152. });
  153. });