import {AutofixDataFixture} from 'sentry-fixture/autofixData'; import {AutofixStepFixture} from 'sentry-fixture/autofixStep'; import {EventFixture} from 'sentry-fixture/event'; import {FrameFixture} from 'sentry-fixture/frame'; import {GroupFixture} from 'sentry-fixture/group'; import {OrganizationFixture} from 'sentry-fixture/organization'; import {ProjectFixture} from 'sentry-fixture/project'; import { render, screen, userEvent, waitFor, waitForElementToBeRemoved, } from 'sentry-test/reactTestingLibrary'; import {t} from 'sentry/locale'; import {EntryType} from 'sentry/types/event'; import {SolutionsHubDrawer} from 'sentry/views/issueDetails/streamline/solutionsHubDrawer'; describe('SolutionsHubDrawer', () => { const organization = OrganizationFixture({ genAIConsent: true, hideAiFeatures: false, features: ['gen-ai-features'], }); const mockEvent = EventFixture({ entries: [ { type: EntryType.EXCEPTION, data: {values: [{stacktrace: {frames: [FrameFixture()]}}]}, }, ], }); const mockGroup = GroupFixture(); const mockProject = ProjectFixture(); const mockAutofixData = AutofixDataFixture({steps: [AutofixStepFixture()]}); beforeEach(() => { MockApiClient.clearMockResponses(); MockApiClient.addMockResponse({ url: `/issues/${mockGroup.id}/autofix/setup/`, body: { genAIConsent: {ok: true}, integration: {ok: true}, githubWriteIntegration: {ok: true}, }, }); MockApiClient.addMockResponse({ url: `/organizations/${mockProject.organization.slug}/issues/${mockGroup.id}/summarize/`, method: 'POST', body: { whatsWrong: 'Test whats wrong', trace: 'Test trace', possibleCause: 'Test possible cause', headline: 'Test headline', }, }); }); it('renders consent state if not consented', async () => { MockApiClient.addMockResponse({ url: `/issues/${mockGroup.id}/autofix/setup/`, body: { genAIConsent: {ok: false}, integration: {ok: false}, githubWriteIntegration: {ok: false}, }, }); MockApiClient.addMockResponse({ url: `/issues/${mockGroup.id}/autofix/`, body: {autofix: null}, }); render( , {organization} ); expect(screen.getByTestId('ai-setup-loading-indicator')).toBeInTheDocument(); await waitForElementToBeRemoved(() => screen.queryByTestId('ai-setup-loading-indicator') ); expect(screen.getByText(mockEvent.id)).toBeInTheDocument(); expect(screen.getByRole('heading', {name: 'Solutions Hub'})).toBeInTheDocument(); expect(screen.getByTestId('ai-setup-data-consent')).toBeInTheDocument(); }); it('renders initial state correctly', async () => { MockApiClient.addMockResponse({ url: `/issues/${mockGroup.id}/autofix/`, body: {autofix: null}, }); render( , {organization} ); expect(screen.getByTestId('ai-setup-loading-indicator')).toBeInTheDocument(); await waitForElementToBeRemoved(() => screen.queryByTestId('ai-setup-loading-indicator') ); expect(screen.getByRole('heading', {name: 'Solutions Hub'})).toBeInTheDocument(); const startButton = screen.getByRole('button', {name: 'Start Autofix'}); expect(startButton).toBeInTheDocument(); }); it('triggers autofix on clicking the Start button', async () => { MockApiClient.addMockResponse({ url: `/issues/${mockGroup.id}/autofix/`, method: 'POST', body: {autofix: null}, }); MockApiClient.addMockResponse({ url: `/issues/${mockGroup.id}/autofix/`, method: 'GET', body: {autofix: null}, }); render( , {organization} ); expect(screen.getByTestId('ai-setup-loading-indicator')).toBeInTheDocument(); await waitForElementToBeRemoved(() => screen.queryByTestId('ai-setup-loading-indicator') ); const startButton = screen.getByRole('button', {name: 'Start Autofix'}); await userEvent.click(startButton); expect( await screen.findByRole('button', {name: t('Start Over')}) ).toBeInTheDocument(); }); it('displays autofix steps and Start Over button when autofixData is available', async () => { MockApiClient.addMockResponse({ url: `/issues/${mockGroup.id}/autofix/`, body: {autofix: mockAutofixData}, }); render( , {organization} ); expect( await screen.findByRole('button', {name: t('Start Over')}) ).toBeInTheDocument(); }); it('resets autofix on clicking the start over button', async () => { MockApiClient.addMockResponse({ url: `/issues/${mockGroup.id}/autofix/`, body: {autofix: mockAutofixData}, }); render( , {organization} ); const startOverButton = await screen.findByRole('button', {name: t('Start Over')}); expect(startOverButton).toBeInTheDocument(); await userEvent.click(startOverButton); await waitFor(() => { expect(screen.getByRole('button', {name: 'Start Autofix'})).toBeInTheDocument(); }); }); it('shows setup if not complete', async () => { MockApiClient.addMockResponse({ url: `/issues/${mockGroup.id}/autofix/setup/`, body: { genAIConsent: {ok: true}, integration: {ok: false}, githubWriteIntegration: {ok: false, repos: []}, }, }); MockApiClient.addMockResponse({ url: `/issues/${mockGroup.id}/autofix/`, body: {autofix: null}, }); render( , {organization} ); expect(screen.getByTestId('ai-setup-loading-indicator')).toBeInTheDocument(); await waitForElementToBeRemoved(() => screen.queryByTestId('ai-setup-loading-indicator') ); expect(screen.getByRole('heading', {name: 'Solutions Hub'})).toBeInTheDocument(); expect(screen.queryByRole('button', {name: 'Start Autofix'})).not.toBeInTheDocument(); expect(await screen.findByText('Install the GitHub Integration')).toBeInTheDocument(); }); });