Skip to content
Open
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,28 @@ import {ProjectsStore} from 'sentry/stores/projectsStore';

describe('ClaudeCodeIntegrationCta', () => {
const project = ProjectFixture();
const enabledProject = ProjectFixture({
...project,
seerScannerAutomation: true,
autofixAutomationTuning: 'medium',
});
const organization = OrganizationFixture({
features: ['integrations-claude-code'],
});

function mockDetailedProject(projectBody = enabledProject) {
return MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${projectBody.slug}/`,
body: projectBody,
});
}

beforeEach(() => {
MockApiClient.clearMockResponses();
localStorage.clear();

mockDetailedProject();

MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${project.slug}/seer/preferences/`,
body: {
Expand Down Expand Up @@ -209,6 +223,7 @@ describe('ClaudeCodeIntegrationCta', () => {
seerScannerAutomation: true,
autofixAutomationTuning: 'medium',
});
mockDetailedProject(projectWithAutomation);

const projectUpdateMock = MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${projectWithAutomation.slug}/`,
Expand Down Expand Up @@ -256,6 +271,7 @@ describe('ClaudeCodeIntegrationCta', () => {
seerScannerAutomation: false,
autofixAutomationTuning: 'off',
});
mockDetailedProject(projectWithoutAutomation);

const updatedProject = {
...projectWithoutAutomation,
Expand Down Expand Up @@ -370,6 +386,7 @@ describe('ClaudeCodeIntegrationCta', () => {
seerScannerAutomation: false,
autofixAutomationTuning: 'off',
});
mockDetailedProject(projectWithoutAutomation);

render(<ClaudeCodeIntegrationCta project={projectWithoutAutomation} />, {
organization,
Expand Down Expand Up @@ -423,6 +440,7 @@ describe('ClaudeCodeIntegrationCta', () => {
seerScannerAutomation: true,
autofixAutomationTuning: 'medium',
});
mockDetailedProject(projectWithAutomation);

render(<ClaudeCodeIntegrationCta project={projectWithAutomation} />, {
organization,
Expand All @@ -440,6 +458,7 @@ describe('ClaudeCodeIntegrationCta', () => {
seerScannerAutomation: true,
autofixAutomationTuning: 'medium',
});
mockDetailedProject(projectWithAutomation);

render(<ClaudeCodeIntegrationCta project={projectWithAutomation} />, {
organization,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {t, tct} from 'sentry/locale';
import {PluginIcon} from 'sentry/plugins/components/pluginIcon';
import type {Project} from 'sentry/types/project';
import {trackAnalytics} from 'sentry/utils/analytics';
import {useDetailedProject} from 'sentry/utils/project/useDetailedProject';
import {useUpdateProject} from 'sentry/utils/project/useUpdateProject';
import {useOrganization} from 'sentry/utils/useOrganization';
import {useUser} from 'sentry/utils/useUser';
Expand All @@ -40,6 +41,16 @@ export function makeCodingAgentIntegrationCta(config: AgentConfig) {
const organization = useOrganization();
const user = useUser();

const hasFeatureFlag =
!config.featureFlag || organization.features.includes(config.featureFlag);
const {data: projectDetails = project, isPending: isLoadingProject} =
useDetailedProject(
{
orgSlug: organization.slug,
projectSlug: project.slug,
},
{enabled: hasFeatureFlag}
);
const {data, isFetching: isLoadingPreferences} = useProjectSeerPreferences(project);
const preference = data?.preference;
const {mutate: updateProjectSeerPreferences, isPending: isUpdatingPreferences} =
Expand All @@ -53,12 +64,10 @@ export function makeCodingAgentIntegrationCta(config: AgentConfig) {
i => i.provider === config.provider
);

const hasFeatureFlag =
!config.featureFlag || organization.features.includes(config.featureFlag);
const hasIntegration = Boolean(integration);
const isAutomationEnabled =
project.seerScannerAutomation !== false &&
project.autofixAutomationTuning !== 'off';
projectDetails.seerScannerAutomation === true &&
projectDetails.autofixAutomationTuning !== 'off';
const isConfigured =
preference?.automation_handoff?.target === config.target && isAutomationEnabled;

Expand Down Expand Up @@ -86,8 +95,8 @@ export function makeCodingAgentIntegrationCta(config: AgentConfig) {
});

const isAutomationDisabled =
project.seerScannerAutomation === false ||
project.autofixAutomationTuning === 'off';
projectDetails.seerScannerAutomation !== true ||
projectDetails.autofixAutomationTuning === 'off';

if (isAutomationDisabled) {
await updateProjectAutomation({
Expand All @@ -111,7 +120,12 @@ export function makeCodingAgentIntegrationCta(config: AgentConfig) {
return null;
}

if (isLoadingPreferences || isLoadingIntegrations || isUpdatingPreferences) {
if (
isLoadingProject ||
isLoadingPreferences ||
isLoadingIntegrations ||
isUpdatingPreferences
) {
return (
<Container
padding="xl"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,26 @@ import {ProjectsStore} from 'sentry/stores/projectsStore';

describe('CursorIntegrationCta', () => {
const project = ProjectFixture();
const enabledProject = ProjectFixture({
...project,
seerScannerAutomation: true,
autofixAutomationTuning: 'medium',
});
const organization = OrganizationFixture();

function mockDetailedProject(projectBody = enabledProject) {
return MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${projectBody.slug}/`,
body: projectBody,
});
}

beforeEach(() => {
MockApiClient.clearMockResponses();
localStorage.clear();

mockDetailedProject();

// Default mock for seer preferences
MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${project.slug}/seer/preferences/`,
Expand Down Expand Up @@ -187,6 +201,7 @@ describe('CursorIntegrationCta', () => {
seerScannerAutomation: false,
autofixAutomationTuning: 'off',
});
mockDetailedProject(projectWithoutAutomation);

const updatedProject = {
...projectWithoutAutomation,
Expand Down Expand Up @@ -271,6 +286,7 @@ describe('CursorIntegrationCta', () => {
seerScannerAutomation: true,
autofixAutomationTuning: 'medium',
});
mockDetailedProject(projectWithAutomation);

const projectUpdateMock = MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${projectWithAutomation.slug}/`,
Expand Down Expand Up @@ -353,6 +369,7 @@ describe('CursorIntegrationCta', () => {
seerScannerAutomation: false,
autofixAutomationTuning: 'off',
});
mockDetailedProject(projectWithoutAutomation);

render(<CursorIntegrationCta project={projectWithoutAutomation} />, {
organization,
Expand Down Expand Up @@ -409,6 +426,7 @@ describe('CursorIntegrationCta', () => {
seerScannerAutomation: true,
autofixAutomationTuning: 'medium',
});
mockDetailedProject(projectWithAutomation);

render(<CursorIntegrationCta project={projectWithAutomation} />, {
organization,
Expand All @@ -426,6 +444,7 @@ describe('CursorIntegrationCta', () => {
seerScannerAutomation: true,
autofixAutomationTuning: 'medium',
});
mockDetailedProject(projectWithAutomation);

render(<CursorIntegrationCta project={projectWithAutomation} />, {
organization,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ export function SeerNotices({groupId, hasGithubIntegration, project}: SeerNotice
);
const {starredViews: views} = useStarredIssueViews();

const detailedProject = useDetailedProject({
const {data: projectDetails = project} = useDetailedProject({
Comment thread
scttcper marked this conversation as resolved.
Outdated
orgSlug: organization.slug,
projectSlug: project.slug,
});
Expand All @@ -125,11 +125,10 @@ export function SeerNotices({groupId, hasGithubIntegration, project}: SeerNotice
const needsRepoSelection =
repos.length === 0 && !preference?.repositories?.length && !codeMappingRepos?.length;
const needsAutomation =
detailedProject?.data &&
(detailedProject?.data?.autofixAutomationTuning === 'off' ||
detailedProject?.data?.autofixAutomationTuning === undefined ||
detailedProject?.data?.seerScannerAutomation === false ||
detailedProject?.data?.seerScannerAutomation === undefined);
projectDetails.autofixAutomationTuning === 'off' ||
projectDetails.autofixAutomationTuning === undefined ||
projectDetails.seerScannerAutomation === false ||
projectDetails.seerScannerAutomation === undefined;
Comment thread
sentry[bot] marked this conversation as resolved.
Outdated
const needsFixabilityView =
!views.some(view => view.query.includes(FieldKey.ISSUE_SEER_ACTIONABILITY)) &&
isStarredViewAllowed;
Expand Down Expand Up @@ -166,8 +165,8 @@ export function SeerNotices({groupId, hasGithubIntegration, project}: SeerNotice
}

const isAutomationDisabled =
project.seerScannerAutomation === false ||
project.autofixAutomationTuning === 'off';
projectDetails.seerScannerAutomation !== true ||
projectDetails.autofixAutomationTuning === 'off';
Comment thread
scttcper marked this conversation as resolved.

if (isAutomationDisabled) {
await updateProjectAutomation({
Expand Down
28 changes: 28 additions & 0 deletions static/app/views/settings/projectSeer/index.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ describe('ProjectSeer', () => {
beforeEach(() => {
project = ProjectFixture();
organization = OrganizationFixture();
MockApiClient.addMockResponse({
url: `/projects/org-slug/${project.slug}/`,
body: project,
});

// Mock the seer setup check endpoint
MockApiClient.addMockResponse({
Expand Down Expand Up @@ -716,6 +720,10 @@ describe('ProjectSeer', () => {
autofixAutomationTuning: 'medium',
seerScannerAutomation: true,
};
MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${project.slug}/`,
body: initialProject,
});

MockApiClient.addMockResponse({
url: `/organizations/${orgWithCursorFeature.slug}/seer/setup-check/`,
Expand Down Expand Up @@ -795,6 +803,10 @@ describe('ProjectSeer', () => {
autofixAutomationTuning: 'medium',
seerScannerAutomation: true,
};
MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${project.slug}/`,
body: initialProject,
});

MockApiClient.addMockResponse({
url: `/organizations/${orgWithCursorFeature.slug}/seer/setup-check/`,
Expand Down Expand Up @@ -903,6 +915,10 @@ describe('ProjectSeer', () => {
autofixAutomationTuning: 'medium',
seerScannerAutomation: true,
};
MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${project.slug}/`,
body: initialProject,
});

MockApiClient.addMockResponse({
url: `/organizations/${orgWithCursorFeature.slug}/seer/setup-check/`,
Expand Down Expand Up @@ -993,6 +1009,10 @@ describe('ProjectSeer', () => {
autofixAutomationTuning: 'medium',
seerScannerAutomation: true,
};
MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${project.slug}/`,
body: initialProject,
});

MockApiClient.addMockResponse({
url: `/organizations/${orgWithCursorFeature.slug}/seer/setup-check/`,
Expand Down Expand Up @@ -1116,6 +1136,10 @@ describe('ProjectSeer', () => {
autofixAutomationTuning: 'medium',
seerScannerAutomation: true,
};
MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${project.slug}/`,
body: initialProject,
});

MockApiClient.addMockResponse({
url: `/organizations/${orgWithBothFeatures.slug}/seer/setup-check/`,
Expand Down Expand Up @@ -1198,6 +1222,10 @@ describe('ProjectSeer', () => {
autofixAutomationTuning: 'medium',
seerScannerAutomation: true,
};
MockApiClient.addMockResponse({
url: `/projects/${organization.slug}/${project.slug}/`,
body: initialProject,
});

MockApiClient.addMockResponse({
url: `/organizations/${orgWithCursorFeature.slug}/seer/setup-check/`,
Expand Down
Loading