diff options
Diffstat (limited to 'server')
4 files changed, 50 insertions, 20 deletions
diff --git a/server/sonar-web/src/main/js/api/components.ts b/server/sonar-web/src/main/js/api/components.ts index 05e518efa37..c9483e53c0b 100644 --- a/server/sonar-web/src/main/js/api/components.ts +++ b/server/sonar-web/src/main/js/api/components.ts @@ -35,6 +35,7 @@ import { SourceLine, SourceViewerFile, } from '../types/types'; +import { AiCodeAssuranceStatus } from './ai-code-assurance'; export interface BaseSearchProjectsParameters { analyzedBefore?: string; @@ -53,8 +54,8 @@ export interface ProjectBase { } export interface ComponentRaw { + aiCodeAssurance?: AiCodeAssuranceStatus; analysisDate?: string; - isAiCodeAssured?: boolean; isAiCodeFixEnabled?: boolean; isFavorite?: boolean; key: string; diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx index 8dd3f3d0e6d..03ad0452ded 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCard.tsx @@ -19,7 +19,7 @@ */ import styled from '@emotion/styled'; -import { Link, LinkStandalone } from '@sonarsource/echoes-react'; +import { IconSparkle, Link, LinkStandalone, Tooltip } from '@sonarsource/echoes-react'; import classNames from 'classnames'; import { isEmpty } from 'lodash'; import { FormattedMessage, useIntl } from 'react-intl'; @@ -41,10 +41,11 @@ import { formatMeasure } from '~sonar-aligned/helpers/measures'; import { Status } from '~sonar-aligned/types/common'; import { ComponentQualifier } from '~sonar-aligned/types/component'; import { MetricKey, MetricType } from '~sonar-aligned/types/metrics'; +import { AiCodeAssuranceStatus } from '../../../../api/ai-code-assurance'; import ChangeInCalculation from '../../../../app/components/ChangeInCalculationPill'; import { useCurrentUser } from '../../../../app/components/current-user/CurrentUserContext'; import Favorite from '../../../../components/controls/Favorite'; -import Tooltip from '../../../../components/controls/Tooltip'; +import AIAssuredIcon, { AiIconColor } from '../../../../components/icon-mappers/AIAssuredIcon'; import DateFromNow from '../../../../components/intl/DateFromNow'; import DateTimeFormatter from '../../../../components/intl/DateTimeFormatter'; import { translate, translateWithParameters } from '../../../../helpers/l10n'; @@ -124,16 +125,33 @@ function renderFirstLine(project: Props['project'], isNewCode: boolean) { </span> </Tooltip> - {project.isAiCodeAssured && ( - <Tooltip content={translate('projects.ai_code.content')}> + {project.aiCodeAssurance === AiCodeAssuranceStatus.CONTAINS_AI_CODE && ( + <Tooltip content={translate('projects.ai_code.tooltip.content')}> <span> - <Badge variant="new" className="sw-ml-2"> + <Badge className="sw-ml-2"> + <IconSparkle className="sw-mr-1 sw-fon" /> {translate('ai_code')} </Badge> </span> </Tooltip> )} + {project.aiCodeAssurance === AiCodeAssuranceStatus.AI_CODE_ASSURED && ( + <Tooltip content={translate('projects.ai_code_assurance.tooltip.content')}> + <span> + <Badge variant="new" className="sw-ml-2"> + <AIAssuredIcon + className="sw-mr-1 sw-align-bottom" + color={AiIconColor.Default} + width={16} + height={16} + /> + {translate('ai_code_assurance')} + </Badge> + </span> + </Tooltip> + )} + {awaitingScan && !isNewCode && !isEmpty(analysisDate) && measures.ncloc !== undefined && ( <ChangeInCalculation qualifier={qualifier} /> )} diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx index 0aad89dfb6f..874b2708a44 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/__tests__/ProjectCard-test.tsx @@ -22,6 +22,7 @@ import { screen, waitFor } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import { ComponentQualifier, Visibility } from '~sonar-aligned/types/component'; import { MetricKey } from '~sonar-aligned/types/metrics'; +import { AiCodeAssuranceStatus } from '../../../../../api/ai-code-assurance'; import { MeasuresServiceMock } from '../../../../../api/mocks/MeasuresServiceMock'; import { ModeServiceMock } from '../../../../../api/mocks/ModeServiceMock'; import { mockComponent } from '../../../../../helpers/mocks/component'; @@ -51,7 +52,19 @@ const PROJECT: Project = { tags: [], visibility: Visibility.Public, isScannable: false, - isAiCodeAssured: true, + aiCodeAssurance: AiCodeAssuranceStatus.AI_CODE_ASSURED, +}; + +const PROJECT_CONTAINS_AI_CODE: Project = { + analysisDate: '2017-01-01', + key: 'foo', + measures: MEASURES, + name: 'Foo', + qualifier: ComponentQualifier.Project, + tags: [], + visibility: Visibility.Public, + isScannable: false, + aiCodeAssurance: AiCodeAssuranceStatus.CONTAINS_AI_CODE, }; const PROJECT_WITH_AI_CODE_DISABLED: Project = { @@ -63,7 +76,7 @@ const PROJECT_WITH_AI_CODE_DISABLED: Project = { tags: [], visibility: Visibility.Public, isScannable: false, - isAiCodeAssured: false, + aiCodeAssurance: AiCodeAssuranceStatus.NONE, }; const USER_LOGGED_OUT = mockCurrentUser(); @@ -95,9 +108,15 @@ it('should display private badge', () => { expect(screen.getByText('visibility.private')).toBeInTheDocument(); }); -it('should display ai code assurance badge when isAiCodeAssured is true', () => { +it('should display ai code assurance badge when ai code assurance is true and quality gate is compliant', () => { const project: Project = { ...PROJECT, visibility: Visibility.Private }; renderProjectCard(project); + expect(screen.getByText('ai_code_assurance')).toBeInTheDocument(); +}); + +it('should display ai code badge when ai code assurance is true and quality gate is not compliant', () => { + const project: Project = { ...PROJECT_CONTAINS_AI_CODE, visibility: Visibility.Private }; + renderProjectCard(project); expect(screen.getByText('ai_code')).toBeInTheDocument(); }); @@ -120,16 +139,7 @@ it('should not display configure analysis button for logged in user and without it('should display applications', () => { renderProjectCard({ ...PROJECT, qualifier: ComponentQualifier.Application }); - expect(screen.getAllByText('qualifier.APP')).toHaveLength(2); -}); - -it('should display 3 projects', () => { - renderProjectCard({ - ...PROJECT, - qualifier: ComponentQualifier.Application, - measures: { ...MEASURES, projects: '3' }, - }); - expect(screen.getByText(/x_projects_.3/)).toBeInTheDocument(); + expect(screen.getByText('qualifier.APP')).toBeInTheDocument(); }); describe('upgrade scenario (awaiting scan)', () => { diff --git a/server/sonar-web/src/main/js/apps/projects/types.ts b/server/sonar-web/src/main/js/apps/projects/types.ts index 23d3f024f9c..691e452b1d3 100644 --- a/server/sonar-web/src/main/js/apps/projects/types.ts +++ b/server/sonar-web/src/main/js/apps/projects/types.ts @@ -19,11 +19,12 @@ */ import { ComponentQualifier, Visibility } from '~sonar-aligned/types/component'; +import { AiCodeAssuranceStatus } from '../../api/ai-code-assurance'; import { Dict } from '../../types/types'; export interface Project { + aiCodeAssurance?: AiCodeAssuranceStatus; analysisDate?: string; - isAiCodeAssured?: boolean; isFavorite?: boolean; isScannable: boolean; key: string; |