diff options
author | Viktor Vorona <viktor.vorona@sonarsource.com> | 2024-08-09 19:20:24 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2024-08-26 20:03:06 +0000 |
commit | 497b7e3d8826e495865b36860ead63a2a4a5b69b (patch) | |
tree | e541f74f9383481645d4db68393726cf57d35174 /server/sonar-web/src/main/js | |
parent | 72492e32e0eadc5ded27b0bf4a4bfb98665dbcc9 (diff) | |
download | sonarqube-497b7e3d8826e495865b36860ead63a2a4a5b69b.tar.gz sonarqube-497b7e3d8826e495865b36860ead63a2a4a5b69b.zip |
SONAR-22717 Change in calculation badge in projects list
Diffstat (limited to 'server/sonar-web/src/main/js')
7 files changed, 323 insertions, 102 deletions
diff --git a/server/sonar-web/src/main/js/app/components/ChangeInCalculationPill.tsx b/server/sonar-web/src/main/js/app/components/ChangeInCalculationPill.tsx new file mode 100644 index 00000000000..bdf31ea2a25 --- /dev/null +++ b/server/sonar-web/src/main/js/app/components/ChangeInCalculationPill.tsx @@ -0,0 +1,57 @@ +/* + * SonarQube + * Copyright (C) 2009-2024 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { Popover } from '@sonarsource/echoes-react'; +import { Pill } from 'design-system'; +import * as React from 'react'; +import DocumentationLink from '../../components/common/DocumentationLink'; +import { DocLink } from '../../helpers/doc-links'; +import { translate } from '../../helpers/l10n'; +import { useIsLegacyCCTMode } from '../../queries/settings'; +import { ComponentQualifier } from '../../sonar-aligned/types/component'; + +interface Props { + qualifier: ComponentQualifier; +} + +export default function ChangeInCalculation({ qualifier }: Readonly<Props>) { + const [isPopoverOpen, setIsPopoverOpen] = React.useState(false); + const { data: isLegacy, isLoading } = useIsLegacyCCTMode(); + + if (isLegacy || isLoading) { + return null; + } + + return ( + <Popover + isOpen={isPopoverOpen} + title={translate('projects.awaiting_scan.title')} + description={translate(`projects.awaiting_scan.description.${qualifier}`)} + footer={ + <DocumentationLink to={DocLink.CleanCodeIntroduction}> + {translate('learn_more')} + </DocumentationLink> + } + > + <Pill variant="info" className="sw-ml-2" onClick={() => setIsPopoverOpen(!isPopoverOpen)}> + {translate('projects.awaiting_scan')} + </Pill> + </Popover> + ); +} diff --git a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx index 0c19a7436ee..8bfe5f83eca 100644 --- a/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx +++ b/server/sonar-web/src/main/js/app/components/ComponentContainer.tsx @@ -34,6 +34,7 @@ import { translateWithParameters } from '../../helpers/l10n'; import { HttpStatus } from '../../helpers/request'; import { getPortfolioUrl, getProjectUrl, getPullRequestUrl } from '../../helpers/urls'; import { useBranchesQuery } from '../../queries/branch'; +import { useIsLegacyCCTMode } from '../../queries/settings'; import { ProjectAlmBindingConfigurationErrors } from '../../types/alm-settings'; import { Branch } from '../../types/branch-like'; import { isFile } from '../../types/component'; @@ -72,6 +73,9 @@ function ComponentContainer({ hasFeature }: Readonly<WithAvailableFeaturesProps> fixedInPullRequest ? component : undefined, ); + //prefetch isLegacyCCTMode + useIsLegacyCCTMode(); + const isInTutorials = pathname.includes('tutorials'); const fetchComponent = React.useCallback( diff --git a/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx index 8a87dad61c3..4f31c39eeb9 100644 --- a/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx +++ b/server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx @@ -51,6 +51,10 @@ jest.mock('../../../api/components', () => ({ .mockResolvedValue({ component: { name: 'component name', analysisDate: '2018-07-30' } }), })); +jest.mock('../../../queries/settings', () => ({ + useIsLegacyCCTMode: jest.fn(), +})); + jest.mock('../../../api/navigation', () => ({ getComponentNavigation: jest.fn().mockResolvedValue({ breadcrumbs: [{ key: 'portfolioKey', name: 'portfolio', qualifier: 'VW' }], 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 d0ff2f6e32f..2ee4a4d6881 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 @@ -42,6 +42,7 @@ 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 ChangeInCalculation from '../../../../app/components/ChangeInCalculationPill'; import Favorite from '../../../../components/controls/Favorite'; import Tooltip from '../../../../components/controls/Tooltip'; import DateFromNow from '../../../../components/intl/DateFromNow'; @@ -67,12 +68,18 @@ function renderFirstLine( isNewCode: boolean, ) { const { analysisDate, isFavorite, key, measures, name, qualifier, tags, visibility } = project; + const noSoftwareQualityMetrics = [ + MetricKey.reliability_issues, + MetricKey.maintainability_issues, + MetricKey.security_issues, + ].every((key) => measures[key] === undefined); + const noRatingMetrics = [ + MetricKey.reliability_rating_new, + MetricKey.sqale_rating_new, + MetricKey.security_rating_new, + ].every((key) => measures[key] === undefined); const awaitingScan = - [ - MetricKey.reliability_issues, - MetricKey.maintainability_issues, - MetricKey.security_issues, - ].every((key) => measures[key] === undefined) && + (noSoftwareQualityMetrics || noRatingMetrics) && !isNewCode && !isEmpty(analysisDate) && measures.ncloc !== undefined; @@ -124,13 +131,7 @@ function renderFirstLine( </Tooltip> {awaitingScan && !isNewCode && !isEmpty(analysisDate) && measures.ncloc !== undefined && ( - <Tooltip content={translate(`projects.awaiting_scan.description.${qualifier}`)}> - <span> - <Badge variant="new" className="sw-ml-2"> - {translate('projects.awaiting_scan')} - </Badge> - </span> - </Tooltip> + <ChangeInCalculation qualifier={qualifier} /> )} </div> diff --git a/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardMeasures.tsx b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardMeasures.tsx index f444465e33b..0f0b31ef799 100644 --- a/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardMeasures.tsx +++ b/server/sonar-web/src/main/js/apps/projects/components/project-card/ProjectCardMeasures.tsx @@ -31,6 +31,7 @@ import RatingComponent from '../../../../app/components/metrics/RatingComponent' import { duplicationRatingConverter } from '../../../../components/measure/utils'; import { translate } from '../../../../helpers/l10n'; import { isDefined } from '../../../../helpers/types'; +import { useIsLegacyCCTMode } from '../../../../queries/settings'; import { Dict } from '../../../../types/types'; import ProjectCardMeasure from './ProjectCardMeasure'; @@ -115,7 +116,7 @@ function renderDuplication(props: ProjectCardMeasuresProps) { ); } -function renderRatings(props: ProjectCardMeasuresProps) { +function renderRatings(props: ProjectCardMeasuresProps, isLegacy: boolean) { const { isNewCode, measures, componentKey } = props; const measuresByCodeLeak = isNewCode @@ -125,27 +126,27 @@ function renderRatings(props: ProjectCardMeasuresProps) { iconLabel: translate(`metric.${MetricKey.security_issues}.short_name`), noShrink: true, metricKey: - measures[MetricKey.security_issues] !== undefined - ? MetricKey.security_issues - : MetricKey.vulnerabilities, + isLegacy || measures[MetricKey.security_issues] === undefined + ? MetricKey.vulnerabilities + : MetricKey.security_issues, metricRatingKey: MetricKey.security_rating, metricType: MetricType.ShortInteger, }, { iconLabel: translate(`metric.${MetricKey.reliability_issues}.short_name`), metricKey: - measures[MetricKey.reliability_issues] !== undefined - ? MetricKey.reliability_issues - : MetricKey.bugs, + isLegacy || measures[MetricKey.reliability_issues] === undefined + ? MetricKey.bugs + : MetricKey.reliability_issues, metricRatingKey: MetricKey.reliability_rating, metricType: MetricType.ShortInteger, }, { iconLabel: translate(`metric.${MetricKey.maintainability_issues}.short_name`), metricKey: - measures[MetricKey.maintainability_issues] !== undefined - ? MetricKey.maintainability_issues - : MetricKey.code_smells, + isLegacy || measures[MetricKey.maintainability_issues] === undefined + ? MetricKey.code_smells + : MetricKey.maintainability_issues, metricRatingKey: MetricKey.sqale_rating, metricType: MetricType.ShortInteger, }, @@ -195,6 +196,7 @@ function renderRatings(props: ProjectCardMeasuresProps) { export default function ProjectCardMeasures(props: ProjectCardMeasuresProps) { const { isNewCode, measures, componentQualifier } = props; + const { data: isLegacy } = useIsLegacyCCTMode(); const { ncloc } = measures; @@ -210,7 +212,7 @@ export default function ProjectCardMeasures(props: ProjectCardMeasuresProps) { const measureList = [ renderNewIssues(props), - ...renderRatings(props), + ...renderRatings(props, !!isLegacy), renderCoverage(props), renderDuplication(props), ].filter(isDefined); 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 3e22f1b4f35..3fb06f240d5 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 @@ -17,13 +17,15 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import { screen } from '@testing-library/react'; +import { screen, waitFor } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; import React from 'react'; import { ComponentQualifier, Visibility } from '~sonar-aligned/types/component'; import { MetricKey } from '~sonar-aligned/types/metrics'; import { MeasuresServiceMock } from '../../../../../api/mocks/MeasuresServiceMock'; import SettingsServiceMock from '../../../../../api/mocks/SettingsServiceMock'; -import { mockCurrentUser, mockLoggedInUser } from '../../../../../helpers/testMocks'; +import { mockComponent } from '../../../../../helpers/mocks/component'; +import { mockCurrentUser, mockLoggedInUser, mockMeasure } from '../../../../../helpers/testMocks'; import { renderComponent } from '../../../../../helpers/testReactTestingUtils'; import { CurrentUser } from '../../../../../types/users'; import { Project } from '../../../types'; @@ -35,6 +37,7 @@ const MEASURES = { [MetricKey.reliability_rating]: '1.0', [MetricKey.security_rating]: '1.0', [MetricKey.sqale_rating]: '1.0', + [MetricKey.security_review_rating]: '1.0', [MetricKey.new_bugs]: '12', }; @@ -94,97 +97,247 @@ it('should display applications', () => { expect(screen.getAllByText('qualifier.APP')).toHaveLength(2); }); -it('should not display awaiting analysis badge and do not display old measures', () => { +it('should display 3 projects', () => { renderProjectCard({ ...PROJECT, - measures: { - ...MEASURES, - [MetricKey.security_issues]: JSON.stringify({ LOW: 0, MEDIUM: 0, HIGH: 1, total: 1 }), - [MetricKey.reliability_issues]: JSON.stringify({ LOW: 0, MEDIUM: 2, HIGH: 0, total: 2 }), - [MetricKey.maintainability_issues]: JSON.stringify({ LOW: 3, MEDIUM: 0, HIGH: 0, total: 3 }), - [MetricKey.code_smells]: '4', - [MetricKey.bugs]: '5', - [MetricKey.vulnerabilities]: '6', - }, + qualifier: ComponentQualifier.Application, + measures: { ...MEASURES, projects: '3' }, }); - expect(screen.queryByText('projects.awaiting_scan')).not.toBeInTheDocument(); - expect(screen.getByText('1')).toBeInTheDocument(); - expect(screen.getByText('2')).toBeInTheDocument(); - expect(screen.getByText('3')).toBeInTheDocument(); - expect(screen.queryByText('4')).not.toBeInTheDocument(); - expect(screen.queryByText('5')).not.toBeInTheDocument(); - expect(screen.queryByText('6')).not.toBeInTheDocument(); + expect(screen.getByText(/x_projects_.3/)).toBeInTheDocument(); }); -it('should display awaiting analysis badge and show the old measures', async () => { - renderProjectCard({ - ...PROJECT, - measures: { - ...MEASURES, - [MetricKey.code_smells]: '4', - [MetricKey.bugs]: '5', - [MetricKey.vulnerabilities]: '6', - }, +describe('upgrade scenario (awaiting scan)', () => { + const oldRatings = { + [MetricKey.reliability_rating]: mockMeasure({ + metric: MetricKey.reliability_rating, + value: '1', + }), + [MetricKey.sqale_rating]: mockMeasure({ + metric: MetricKey.sqale_rating, + value: '1', + }), + [MetricKey.security_rating]: mockMeasure({ + metric: MetricKey.security_rating, + value: '1', + }), + [MetricKey.security_review_rating]: mockMeasure({ + metric: MetricKey.security_review_rating, + value: '1', + }), + }; + + const newRatings = { + [MetricKey.reliability_rating_new]: mockMeasure({ + metric: MetricKey.reliability_rating_new, + value: '2', + }), + [MetricKey.sqale_rating_new]: mockMeasure({ + metric: MetricKey.sqale_rating_new, + value: '2', + }), + [MetricKey.security_rating_new]: mockMeasure({ + metric: MetricKey.security_rating_new, + value: '2', + }), + [MetricKey.security_review_rating_new]: mockMeasure({ + metric: MetricKey.security_review_rating_new, + value: '2', + }), + }; + beforeEach(() => { + measuresHandler.setComponents({ + component: mockComponent({ key: PROJECT.key }), + ancestors: [], + children: [], + }); + measuresHandler.registerComponentMeasures({ + [PROJECT.key]: oldRatings, + }); + }); + it('should not display awaiting analysis badge and do not display old measures', async () => { + measuresHandler.registerComponentMeasures({ + [PROJECT.key]: newRatings, + }); + renderProjectCard({ + ...PROJECT, + measures: { + ...MEASURES, + [MetricKey.security_issues]: JSON.stringify({ LOW: 0, MEDIUM: 0, HIGH: 1, total: 1 }), + [MetricKey.reliability_issues]: JSON.stringify({ LOW: 0, MEDIUM: 2, HIGH: 0, total: 2 }), + [MetricKey.maintainability_issues]: JSON.stringify({ + LOW: 3, + MEDIUM: 0, + HIGH: 0, + total: 3, + }), + [MetricKey.sqale_rating_new]: '2', + [MetricKey.reliability_rating_new]: '2', + [MetricKey.security_rating_new]: '2', + [MetricKey.security_review_rating_new]: '2', + [MetricKey.code_smells]: '4', + [MetricKey.bugs]: '5', + [MetricKey.vulnerabilities]: '6', + }, + }); + expect(screen.getByText('1')).toBeInTheDocument(); + expect(screen.getByText('2')).toBeInTheDocument(); + expect(screen.getByText('3')).toBeInTheDocument(); + await waitFor(() => expect(screen.getAllByText('B')).toHaveLength(4)); + expect(screen.queryByText('projects.awaiting_scan')).not.toBeInTheDocument(); + expect(screen.queryByText('4')).not.toBeInTheDocument(); + expect(screen.queryByText('5')).not.toBeInTheDocument(); + expect(screen.queryByText('6')).not.toBeInTheDocument(); + expect(screen.queryByText('A')).not.toBeInTheDocument(); }); - expect(screen.getByText('projects.awaiting_scan')).toBeInTheDocument(); - await expect(screen.getByText('projects.awaiting_scan')).toHaveATooltipWithContent( - 'projects.awaiting_scan.description.TRK', - ); - expect(screen.getByText('4')).toBeInTheDocument(); - expect(screen.getByText('5')).toBeInTheDocument(); - expect(screen.getByText('6')).toBeInTheDocument(); -}); -it('should display awaiting analysis badge and show the old measures for Application', async () => { - renderProjectCard({ - ...PROJECT, - qualifier: ComponentQualifier.Application, - measures: { - ...MEASURES, - [MetricKey.code_smells]: '4', - [MetricKey.bugs]: '5', - [MetricKey.vulnerabilities]: '6', - }, + it('should display awaiting analysis badge and show the old measures', async () => { + const user = userEvent.setup(); + renderProjectCard({ + ...PROJECT, + measures: { + ...MEASURES, + [MetricKey.code_smells]: '4', + [MetricKey.bugs]: '5', + [MetricKey.vulnerabilities]: '6', + }, + }); + expect(await screen.findByText('projects.awaiting_scan')).toBeInTheDocument(); + await user.click(screen.getByText('projects.awaiting_scan')); + await expect(screen.getByText('projects.awaiting_scan.description.TRK')).toBeInTheDocument(); + expect(screen.getByText('4')).toBeInTheDocument(); + expect(screen.getByText('5')).toBeInTheDocument(); + expect(screen.getByText('6')).toBeInTheDocument(); + expect(screen.getAllByText('A')).toHaveLength(4); }); - expect(screen.getByText('projects.awaiting_scan')).toBeInTheDocument(); - await expect(screen.getByText('projects.awaiting_scan')).toHaveATooltipWithContent( - 'projects.awaiting_scan.description.APP', - ); - expect(screen.getByText('4')).toBeInTheDocument(); - expect(screen.getByText('5')).toBeInTheDocument(); - expect(screen.getByText('6')).toBeInTheDocument(); -}); -it('should not display awaiting analysis badge if project is not analyzed', () => { - renderProjectCard({ - ...PROJECT, - analysisDate: undefined, + it('should display awaiting analysis badge, show new software qualities, but old ratings', async () => { + renderProjectCard({ + ...PROJECT, + measures: { + ...MEASURES, + [MetricKey.security_issues]: JSON.stringify({ LOW: 0, MEDIUM: 0, HIGH: 1, total: 1 }), + [MetricKey.reliability_issues]: JSON.stringify({ LOW: 0, MEDIUM: 2, HIGH: 0, total: 2 }), + [MetricKey.maintainability_issues]: JSON.stringify({ + LOW: 3, + MEDIUM: 0, + HIGH: 0, + total: 3, + }), + [MetricKey.code_smells]: '4', + [MetricKey.bugs]: '5', + [MetricKey.vulnerabilities]: '6', + }, + }); + expect(await screen.findByText('projects.awaiting_scan')).toBeInTheDocument(); + expect(screen.getByText('1')).toBeInTheDocument(); + expect(screen.getByText('2')).toBeInTheDocument(); + expect(screen.getByText('3')).toBeInTheDocument(); + expect(screen.queryByText('4')).not.toBeInTheDocument(); + expect(screen.queryByText('5')).not.toBeInTheDocument(); + expect(screen.queryByText('6')).not.toBeInTheDocument(); + await waitFor(() => expect(screen.getAllByText('A')).toHaveLength(4)); }); - expect(screen.queryByText('projects.awaiting_scan')).not.toBeInTheDocument(); -}); -it('should not display awaiting analysis badge if project does not have lines of code', () => { - renderProjectCard({ - ...PROJECT, - measures: { - ...(({ [MetricKey.ncloc]: _, ...rest }) => rest)(MEASURES), - }, + it('should display awaiting analysis badge and show the old measures for Application', async () => { + const user = userEvent.setup(); + renderProjectCard({ + ...PROJECT, + qualifier: ComponentQualifier.Application, + measures: { + ...MEASURES, + [MetricKey.code_smells]: '4', + [MetricKey.bugs]: '5', + [MetricKey.vulnerabilities]: '6', + }, + }); + expect(await screen.findByText('projects.awaiting_scan')).toBeInTheDocument(); + await user.click(screen.getByText('projects.awaiting_scan')); + await expect(screen.getByText('projects.awaiting_scan.description.APP')).toBeInTheDocument(); + expect(screen.getByText('4')).toBeInTheDocument(); + expect(screen.getByText('5')).toBeInTheDocument(); + expect(screen.getByText('6')).toBeInTheDocument(); }); - expect(screen.queryByText('projects.awaiting_scan')).not.toBeInTheDocument(); -}); -it('should not display awaiting analysis badge if it is a new code filter', () => { - renderProjectCard(PROJECT, undefined, 'leak'); - expect(screen.queryByText('projects.awaiting_scan')).not.toBeInTheDocument(); -}); + it('should not display awaiting analysis badge if project is not analyzed', () => { + renderProjectCard({ + ...PROJECT, + analysisDate: undefined, + }); + expect(screen.queryByText('projects.awaiting_scan')).not.toBeInTheDocument(); + }); -it('should display 3 aplication', () => { - renderProjectCard({ - ...PROJECT, - qualifier: ComponentQualifier.Application, - measures: { ...MEASURES, projects: '3' }, + it('should not display awaiting analysis badge if project does not have lines of code', () => { + renderProjectCard({ + ...PROJECT, + measures: { + ...(({ [MetricKey.ncloc]: _, ...rest }) => rest)(MEASURES), + }, + }); + expect(screen.queryByText('projects.awaiting_scan')).not.toBeInTheDocument(); + }); + + it('should not display awaiting analysis badge if it is a new code filter', () => { + renderProjectCard(PROJECT, undefined, 'leak'); + expect(screen.queryByText('projects.awaiting_scan')).not.toBeInTheDocument(); + }); + + it('should not display awaiting analysis badge if legacy mode is enabled', async () => { + settingsHandler.set('sonar.legacy.ratings.mode.enabled', 'true'); + renderProjectCard({ + ...PROJECT, + measures: { + ...MEASURES, + [MetricKey.code_smells]: '4', + [MetricKey.bugs]: '5', + [MetricKey.vulnerabilities]: '6', + }, + }); + expect(screen.getByText('4')).toBeInTheDocument(); + expect(screen.getByText('5')).toBeInTheDocument(); + expect(screen.getByText('6')).toBeInTheDocument(); + await waitFor(() => expect(screen.getAllByText('A')).toHaveLength(4)); + expect(screen.queryByText('projects.awaiting_scan')).not.toBeInTheDocument(); + }); + + it('should not display new values if legacy mode is enabled', async () => { + settingsHandler.set('sonar.legacy.ratings.mode.enabled', 'true'); + measuresHandler.registerComponentMeasures({ + [PROJECT.key]: { + ...newRatings, + ...oldRatings, + }, + }); + renderProjectCard({ + ...PROJECT, + measures: { + ...MEASURES, + [MetricKey.security_issues]: JSON.stringify({ LOW: 0, MEDIUM: 0, HIGH: 1, total: 1 }), + [MetricKey.reliability_issues]: JSON.stringify({ LOW: 0, MEDIUM: 2, HIGH: 0, total: 2 }), + [MetricKey.maintainability_issues]: JSON.stringify({ + LOW: 3, + MEDIUM: 0, + HIGH: 0, + total: 3, + }), + [MetricKey.sqale_rating_new]: '2', + [MetricKey.reliability_rating_new]: '2', + [MetricKey.security_rating_new]: '2', + [MetricKey.security_review_rating_new]: '2', + [MetricKey.code_smells]: '4', + [MetricKey.bugs]: '5', + [MetricKey.vulnerabilities]: '6', + }, + }); + expect(await screen.findByText('4')).toBeInTheDocument(); + expect(screen.getByText('5')).toBeInTheDocument(); + expect(screen.getByText('6')).toBeInTheDocument(); + expect(screen.queryByText('1')).not.toBeInTheDocument(); + expect(screen.queryByText('2')).not.toBeInTheDocument(); + expect(screen.queryByText('3')).not.toBeInTheDocument(); + await waitFor(() => expect(screen.getAllByText('A')).toHaveLength(4)); + expect(screen.queryByText('B')).not.toBeInTheDocument(); + expect(screen.queryByText('projects.awaiting_scan')).not.toBeInTheDocument(); }); - expect(screen.getByText(/x_projects_.3/)).toBeInTheDocument(); }); function renderProjectCard(project: Project, user: CurrentUser = USER_LOGGED_OUT, type?: string) { diff --git a/server/sonar-web/src/main/js/queries/settings.ts b/server/sonar-web/src/main/js/queries/settings.ts index 2afb4624194..f6534ee40b8 100644 --- a/server/sonar-web/src/main/js/queries/settings.ts +++ b/server/sonar-web/src/main/js/queries/settings.ts @@ -49,7 +49,7 @@ export const useGetValueQuery = createQueryHook( export const useIsLegacyCCTMode = () => { return useGetValueQuery( { key: 'sonar.legacy.ratings.mode.enabled' }, - { staleTime: Infinity, select: (data) => !!data }, + { staleTime: Infinity, select: (data) => data?.value === 'true' }, ); }; |