Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 6 additions & 7 deletions static/app/components/events/autofix/autofixSetupModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import {
useAutofixSetup,
} from 'sentry/components/events/autofix/useAutofixSetup';
import {GuidedSteps} from 'sentry/components/guidedSteps/guidedSteps';
import HookOrDefault from 'sentry/components/hookOrDefault';
import ExternalLink from 'sentry/components/links/externalLink';
import LoadingError from 'sentry/components/loadingError';
import LoadingIndicator from 'sentry/components/loadingIndicator';
Expand All @@ -18,11 +17,6 @@ import {space} from 'sentry/styles/space';
import {trackAnalytics} from 'sentry/utils/analytics';
import useOrganization from 'sentry/utils/useOrganization';

const ConsentStep = HookOrDefault({
hookName: 'component:autofix-setup-step-consent',
defaultComponent: null,
});

function AutofixIntegrationStep({autofixSetup}: {autofixSetup: AutofixSetupResponse}) {
if (autofixSetup.integration.ok) {
return (
Expand Down Expand Up @@ -283,7 +277,6 @@ function AutofixSetupSteps({
}) {
return (
<GuidedSteps>
<ConsentStep hasConsented={autofixSetup.genAIConsent.ok} />
<GuidedSteps.Step
stepKey="integration"
title={t('Install the GitHub Integration')}
Expand Down Expand Up @@ -349,6 +342,7 @@ export function AutofixSetupContent({

return (
<Fragment>
<Divider />
<Header>Set up Autofix</Header>
<p>
Sentry's AI-enabled Autofix uses all of the contextual data surrounding this error
Expand Down Expand Up @@ -401,3 +395,8 @@ const GithubLink = styled('div')`
align-items: center;
gap: ${space(0.5)};
`;

const Divider = styled('div')`
margin: ${space(3)} 0;
border-bottom: 2px solid ${p => p.theme.gray100};
`;
4 changes: 4 additions & 0 deletions static/app/types/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ export type RouteHooks = {
* Component specific hooks for DateRange and SelectorItems
* These components have plan specific overrides in getsentry
*/
type AiSetupDataConsentProps = {
groupId: string;
};
type AutofixSetupConsentStepProps = {hasConsented: boolean};
type DateRangeProps = React.ComponentProps<typeof DateRange>;

Expand Down Expand Up @@ -184,6 +187,7 @@ export type MembershipSettingsProps = {
* Component wrapping hooks
*/
export type ComponentHooks = {
'component:ai-setup-data-consent': () => React.ComponentType<AiSetupDataConsentProps> | null;
'component:autofix-setup-step-consent': () => React.ComponentType<AutofixSetupConsentStepProps> | null;
'component:codecov-integration-settings-link': () => React.ComponentType<CodecovLinkProps>;
'component:confirm-account-close': () => React.ComponentType<AttemptCloseAttemptProps>;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,19 @@ import {GroupFixture} from 'sentry-fixture/group';
import {OrganizationFixture} from 'sentry-fixture/organization';
import {ProjectFixture} from 'sentry-fixture/project';

import {render, screen, userEvent, waitFor} from 'sentry-test/reactTestingLibrary';
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('AutofixDrawer', () => {
describe('SolutionsHubDrawer', () => {
const organization = OrganizationFixture({genAIConsent: true, hideAiFeatures: false});

const mockEvent = EventFixture({
Expand Down Expand Up @@ -51,18 +57,54 @@ describe('AutofixDrawer', () => {
});
});

it('renders properly', () => {
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: mockAutofixData},
body: {autofix: null},
});

render(
<SolutionsHubDrawer event={mockEvent} group={mockGroup} project={mockProject} />,
{organization}
);

expect(screen.getByText(mockGroup.shortId)).toBeInTheDocument();
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(
<SolutionsHubDrawer event={mockEvent} group={mockGroup} project={mockProject} />,
{organization}
);

expect(screen.getByTestId('ai-setup-loading-indicator')).toBeInTheDocument();

await waitForElementToBeRemoved(() =>
screen.queryByTestId('ai-setup-loading-indicator')
);

expect(screen.getByText(mockEvent.id)).toBeInTheDocument();

Expand All @@ -89,6 +131,12 @@ describe('AutofixDrawer', () => {
{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);

Expand Down
89 changes: 49 additions & 40 deletions static/app/views/issueDetails/streamline/solutionsHubDrawer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ import {useAiAutofix} from 'sentry/components/events/autofix/useAutofix';
import {useAutofixSetup} from 'sentry/components/events/autofix/useAutofixSetup';
import {DrawerBody, DrawerHeader} from 'sentry/components/globalDrawer/components';
import {GroupSummaryBody, useGroupSummary} from 'sentry/components/group/groupSummary';
import HookOrDefault from 'sentry/components/hookOrDefault';
import Input from 'sentry/components/input';
import LoadingIndicator from 'sentry/components/loadingIndicator';
import {IconDocs, IconSeer} from 'sentry/icons';
import {t, tct} from 'sentry/locale';
import {space} from 'sentry/styles/space';
Expand Down Expand Up @@ -88,6 +90,7 @@ function AutofixStartBox({onSend, groupId}: AutofixStartBoxProps) {
: 'Autofix: Start Fix Clicked'
}
analyticsParams={{group_id: groupId}}
aria-label="Start Autofix"
>
{t('Start Autofix')}
</Button>
Expand Down Expand Up @@ -133,8 +136,13 @@ interface SolutionsHubDrawerProps {
project: Project;
}

const AiSetupDataConsent = HookOrDefault({
hookName: 'component:ai-setup-data-consent',
defaultComponent: () => <div data-test-id="ai-setup-data-consent" />,
});

export function SolutionsHubDrawer({group, project, event}: SolutionsHubDrawerProps) {
const {autofixData, triggerAutofix, reset, isPolling} = useAiAutofix(group, event);
const {autofixData, triggerAutofix, reset} = useAiAutofix(group, event);
const {
data: summaryData,
isError,
Expand All @@ -154,11 +162,14 @@ export function SolutionsHubDrawer({group, project, event}: SolutionsHubDrawerPr

const config = getConfigForIssueType(group, project);

const isSetupComplete = setupData?.integration.ok && setupData?.genAIConsent.ok;
const hasSummary = summaryData && !isError && setupData?.genAIConsent.ok;
const hasConsent = Boolean(setupData?.genAIConsent.ok);
const isAutofixSetupComplete = setupData?.integration.ok && hasConsent;

const hasSummary = summaryData && !isError && hasConsent;

const organization = useOrganization();
const isSampleError = useIsSampleEvent();

const displayAiAutofix =
shouldDisplayAiAutofixForOrganization(organization) &&
config.autofix &&
Expand Down Expand Up @@ -234,35 +245,43 @@ export function SolutionsHubDrawer({group, project, event}: SolutionsHubDrawerPr
</ButtonBar>
)}
</HeaderText>
{hasSummary && (
<StyledCard>
<GroupSummaryBody
data={summaryData}
isError={isError}
isPending={isSummaryLoading}
/>
</StyledCard>
)}
{displayAiAutofix && (
{isSetupLoading ? (
<div data-test-id="ai-setup-loading-indicator">
<LoadingIndicator />
</div>
) : !hasConsent ? (
<AiSetupDataConsent groupId={group.id} />
) : (
<Fragment>
{!isSetupLoading && !isSetupComplete ? (
<SetupContainer>
<AutofixSetupContent
projectId={project.id}
groupId={group.id}
onComplete={refetchSetup}
{hasSummary && (
<StyledCard>
<GroupSummaryBody
data={summaryData}
isError={isError}
isPending={isSummaryLoading}
/>
</SetupContainer>
) : !autofixData && isPolling ? (
<AutofixStartBox onSend={triggerAutofix} groupId={group.id} />
) : autofixData ? (
<AutofixSteps
data={autofixData}
groupId={group.id}
runId={autofixData.run_id}
onRetry={reset}
/>
) : null}
</StyledCard>
)}
{displayAiAutofix && (
<Fragment>
{!isAutofixSetupComplete ? (
<AutofixSetupContent
groupId={group.id}
projectId={project.id}
onComplete={refetchSetup}
/>
) : !autofixData ? (
<AutofixStartBox onSend={triggerAutofix} groupId={group.id} />
) : (
<AutofixSteps
data={autofixData}
groupId={group.id}
runId={autofixData.run_id}
onRetry={reset}
/>
)}
</Fragment>
)}
</Fragment>
)}
</SolutionsDrawerBody>
Expand Down Expand Up @@ -392,16 +411,6 @@ const StarLarge3 = styled(StarLarge)`
height: 28px;
`;

const SetupContainer = styled('div')`
padding: ${space(2)};

/* Override some modal-specific styles */
h3 {
font-size: ${p => p.theme.fontSizeLarge};
margin-bottom: ${space(2)};
}
`;

const StyledCard = styled('div')`
background: ${p => p.theme.backgroundElevated};
border-radius: ${p => p.theme.borderRadius};
Expand Down
Loading