aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'server/sonar-web/src/main')
-rw-r--r--server/sonar-web/src/main/js/api/components.ts2
-rw-r--r--server/sonar-web/src/main/js/app/styles/init/misc.css8
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectCard.tsx170
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeak.tsx124
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverall.tsx112
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.tsx12
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx12
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCard-test.tsx95
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeak-test.tsx105
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverall-test.tsx108
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverallMeasures-test.tsx72
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap6
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCard-test.tsx.snap455
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeak-test.tsx.snap275
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverall-test.tsx.snap273
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.tsx.snap12
-rw-r--r--server/sonar-web/src/main/js/apps/projects/styles.css6
-rw-r--r--server/sonar-web/src/main/js/apps/projects/types.ts4
-rw-r--r--server/sonar-web/src/main/js/apps/projects/utils.ts86
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/Risk.tsx17
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.tsx17
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Risk-test.tsx2
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/SimpleBubbleChart-test.tsx11
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap2
-rw-r--r--server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap57
25 files changed, 929 insertions, 1114 deletions
diff --git a/server/sonar-web/src/main/js/api/components.ts b/server/sonar-web/src/main/js/api/components.ts
index 684308a8dbc..df296aa6181 100644
--- a/server/sonar-web/src/main/js/api/components.ts
+++ b/server/sonar-web/src/main/js/api/components.ts
@@ -20,6 +20,7 @@
import { getJSON, post, postJSON, RequestData } from 'sonar-ui-common/helpers/request';
import throwGlobalError from '../app/utils/throwGlobalError';
import { BranchParameters } from '../types/branch-like';
+import { ComponentQualifier } from '../types/component';
export interface BaseSearchProjectsParameters {
analyzedBefore?: string;
@@ -207,6 +208,7 @@ export interface Component {
name: string;
isFavorite?: boolean;
analysisDate?: string;
+ qualifier: ComponentQualifier;
tags: string[];
visibility: T.Visibility;
leakPeriodDate?: string;
diff --git a/server/sonar-web/src/main/js/app/styles/init/misc.css b/server/sonar-web/src/main/js/app/styles/init/misc.css
index d6ec455fff5..83b3148fd36 100644
--- a/server/sonar-web/src/main/js/app/styles/init/misc.css
+++ b/server/sonar-web/src/main/js/app/styles/init/misc.css
@@ -144,10 +144,18 @@ th.hide-overflow {
padding-top: var(--gridSize) !important;
}
+.padded-right {
+ padding-right: var(--gridSize) !important;
+}
+
.padded-bottom {
padding-bottom: var(--gridSize) !important;
}
+.padded-left {
+ padding-left: var(--gridSize) !important;
+}
+
.little-padded-top {
padding-top: calc(var(--gridSize) / 2) !important;
}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCard.tsx b/server/sonar-web/src/main/js/apps/projects/components/ProjectCard.tsx
index f2494aa86ac..bf49fab53fe 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCard.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCard.tsx
@@ -17,10 +17,25 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import * as classNames from 'classnames';
+import * as difference from 'date-fns/difference_in_milliseconds';
import * as React from 'react';
+import { Link } from 'react-router';
+import QualifierIcon from 'sonar-ui-common/components/icons/QualifierIcon';
+import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
+import PrivacyBadgeContainer from '../../../components/common/PrivacyBadgeContainer';
+import Favorite from '../../../components/controls/Favorite';
+import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
+import TagsList from '../../../components/tags/TagsList';
+import { getProjectUrl } from '../../../helpers/urls';
+import { isLoggedIn } from '../../../helpers/users';
+import { ComponentQualifier } from '../../../types/component';
import { Project } from '../types';
-import ProjectCardLeak from './ProjectCardLeak';
-import ProjectCardOverall from './ProjectCardOverall';
+import { formatDuration } from '../utils';
+import ProjectCardLeakMeasures from './ProjectCardLeakMeasures';
+import ProjectCardOrganizationContainer from './ProjectCardOrganizationContainer';
+import ProjectCardOverallMeasures from './ProjectCardOverallMeasures';
+import ProjectCardQualityGate from './ProjectCardQualityGate';
interface Props {
currentUser: T.CurrentUser;
@@ -31,11 +46,150 @@ interface Props {
type?: string;
}
-export default class ProjectCard extends React.PureComponent<Props> {
- render() {
- if (this.props.type === 'leak') {
- return <ProjectCardLeak {...this.props} />;
- }
- return <ProjectCardOverall {...this.props} />;
+interface Dates {
+ analysisDate: string;
+ leakPeriodDate?: string;
+}
+
+function getDates(project: Project, type: string | undefined) {
+ const { analysisDate, leakPeriodDate } = project;
+ if (!analysisDate || (type === 'leak' && !leakPeriodDate)) {
+ return undefined;
+ } else {
+ return { analysisDate, leakPeriodDate };
}
}
+
+function renderHeader(props: Props) {
+ const { organization, project } = props;
+ const hasTags = project.tags.length > 0;
+ return (
+ <div className="project-card-header">
+ {project.isFavorite !== undefined && (
+ <Favorite
+ className="spacer-right"
+ component={project.key}
+ favorite={project.isFavorite}
+ handleFavorite={props.handleFavorite}
+ qualifier={project.qualifier}
+ />
+ )}
+ <h2 className="project-card-name">
+ {!organization && <ProjectCardOrganizationContainer organization={project.organization} />}
+ <Link to={getProjectUrl(project.key)}>{project.name}</Link>
+ </h2>
+ {project.analysisDate && <ProjectCardQualityGate status={project.measures['alert_status']} />}
+ <div className="project-card-header-right">
+ <PrivacyBadgeContainer
+ className="spacer-left"
+ organization={organization || project.organization}
+ qualifier={project.qualifier}
+ tooltipProps={{ projectKey: project.key }}
+ visibility={project.visibility}
+ />
+
+ {hasTags && <TagsList className="spacer-left note" tags={project.tags} />}
+ </div>
+ </div>
+ );
+}
+
+function renderDates(dates: Dates, type: string | undefined) {
+ const { analysisDate, leakPeriodDate } = dates;
+ const periodMs = leakPeriodDate ? difference(Date.now(), leakPeriodDate) : 0;
+
+ return (
+ <>
+ <DateTimeFormatter date={analysisDate}>
+ {formattedDate => (
+ <span className="note">
+ {translateWithParameters('projects.last_analysis_on_x', formattedDate)}
+ </span>
+ )}
+ </DateTimeFormatter>
+ {type === 'leak' && periodMs !== undefined && (
+ <span className="project-card-leak-date big-spacer-left big-spacer-right">
+ {translateWithParameters('projects.new_code_period_x', formatDuration(periodMs))}
+ </span>
+ )}
+ </>
+ );
+}
+
+function renderDateRow(project: Project, dates: Dates | undefined, type: string | undefined) {
+ if (project.qualifier === ComponentQualifier.Application || dates) {
+ return (
+ <div
+ className={classNames('display-flex-center project-card-dates spacer-top', {
+ 'big-spacer-left padded-left': project.isFavorite !== undefined
+ })}>
+ {dates && renderDates(dates, type)}
+
+ {project.qualifier === ComponentQualifier.Application && (
+ <div className="text-right flex-1-0-auto">
+ <QualifierIcon className="spacer-right" qualifier={project.qualifier} />
+ {translate('qualifier.APP')}
+ {project.measures.projects && (
+ <>
+ {' ‒ '}
+ {translateWithParameters('x_projects_', project.measures.projects)}
+ </>
+ )}
+ </div>
+ )}
+ </div>
+ );
+ } else {
+ return null;
+ }
+}
+
+function renderMeasures(props: Props, dates: Dates | undefined) {
+ const { currentUser, project, type } = props;
+
+ const { measures } = project;
+
+ if (dates) {
+ return type === 'leak' ? (
+ <ProjectCardLeakMeasures measures={measures} />
+ ) : (
+ <ProjectCardOverallMeasures componentQualifier={project.qualifier} measures={measures} />
+ );
+ } else {
+ return (
+ <div className="project-card-not-analyzed">
+ <span className="note">
+ {type === 'leak' && project.analysisDate
+ ? translate('projects.no_new_code_period', project.qualifier)
+ : translate('projects.not_analyzed', project.qualifier)}
+ </span>
+ {project.qualifier !== ComponentQualifier.Application &&
+ !project.analysisDate &&
+ isLoggedIn(currentUser) && (
+ <Link className="button spacer-left" to={getProjectUrl(project.key)}>
+ {translate('projects.configure_analysis')}
+ </Link>
+ )}
+ </div>
+ );
+ }
+}
+
+export default function ProjectCard(props: Props) {
+ const { height, project, type } = props;
+
+ const dates = getDates(project, type);
+
+ return (
+ <div
+ className="boxed-group project-card big-padded display-flex-column display-flex-space-between"
+ data-key={project.key}
+ style={{ height }}>
+ <div>
+ {renderHeader(props)}
+ {renderDateRow(project, dates, type)}
+ </div>
+ {renderMeasures(props, dates)}
+ </div>
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeak.tsx b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeak.tsx
deleted file mode 100644
index cfec74b9366..00000000000
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardLeak.tsx
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 * as difference from 'date-fns/difference_in_milliseconds';
-import * as React from 'react';
-import { Link } from 'react-router';
-import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
-import PrivacyBadgeContainer from '../../../components/common/PrivacyBadgeContainer';
-import Favorite from '../../../components/controls/Favorite';
-import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
-import TagsList from '../../../components/tags/TagsList';
-import { getProjectUrl } from '../../../helpers/urls';
-import { isLoggedIn } from '../../../helpers/users';
-import { Project } from '../types';
-import { formatDuration } from '../utils';
-import ProjectCardLeakMeasures from './ProjectCardLeakMeasures';
-import ProjectCardOrganizationContainer from './ProjectCardOrganizationContainer';
-import ProjectCardQualityGate from './ProjectCardQualityGate';
-
-interface Props {
- currentUser: T.CurrentUser;
- handleFavorite: (component: string, isFavorite: boolean) => void;
- height: number;
- organization: T.Organization | undefined;
- project: Project;
-}
-
-export default class ProjectCardLeak extends React.PureComponent<Props> {
- render() {
- const { currentUser, handleFavorite, height, organization, project } = this.props;
- const { measures } = project;
- const hasTags = project.tags.length > 0;
- const periodMs = project.leakPeriodDate ? difference(Date.now(), project.leakPeriodDate) : 0;
-
- return (
- <div className="boxed-group project-card" data-key={project.key} style={{ height }}>
- <div className="boxed-group-header clearfix">
- <div className="project-card-header">
- {project.isFavorite != null && (
- <Favorite
- className="spacer-right"
- component={project.key}
- favorite={project.isFavorite}
- handleFavorite={handleFavorite}
- qualifier="TRK"
- />
- )}
- <h2 className="project-card-name">
- {!organization && (
- <ProjectCardOrganizationContainer organization={project.organization} />
- )}
- <Link to={{ pathname: '/dashboard', query: { id: project.key } }}>
- {project.name}
- </Link>
- </h2>
- {project.analysisDate && <ProjectCardQualityGate status={measures['alert_status']} />}
- <div className="project-card-header-right">
- <PrivacyBadgeContainer
- className="spacer-left"
- organization={organization || project.organization}
- qualifier="TRK"
- tooltipProps={{ projectKey: project.key }}
- visibility={project.visibility}
- />
-
- {hasTags && <TagsList className="spacer-left note" tags={project.tags} />}
- </div>
- </div>
- {project.analysisDate && project.leakPeriodDate && (
- <div className="project-card-dates note text-right pull-right">
- <span className="project-card-leak-date pull-right">
- {translateWithParameters('projects.new_code_period_x', formatDuration(periodMs))}
- </span>
- <DateTimeFormatter date={project.analysisDate}>
- {formattedDate => (
- <span>
- {translateWithParameters('projects.last_analysis_on_x', formattedDate)}
- </span>
- )}
- </DateTimeFormatter>
- </div>
- )}
- </div>
-
- {project.analysisDate && project.leakPeriodDate ? (
- <div className="boxed-group-inner">
- <ProjectCardLeakMeasures measures={measures} />
- </div>
- ) : (
- <div className="boxed-group-inner">
- <div className="project-card-not-analyzed">
- <span className="note">
- {project.analysisDate
- ? translate('projects.no_new_code_period')
- : translate('projects.not_analyzed')}
- </span>
- {!project.analysisDate && isLoggedIn(currentUser) && (
- <Link className="button spacer-left" to={getProjectUrl(project.key)}>
- {translate('projects.configure_analysis')}
- </Link>
- )}
- </div>
- </div>
- )}
- </div>
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverall.tsx b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverall.tsx
deleted file mode 100644
index f918839d035..00000000000
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverall.tsx
+++ /dev/null
@@ -1,112 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 * as React from 'react';
-import { Link } from 'react-router';
-import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
-import PrivacyBadgeContainer from '../../../components/common/PrivacyBadgeContainer';
-import Favorite from '../../../components/controls/Favorite';
-import DateTimeFormatter from '../../../components/intl/DateTimeFormatter';
-import TagsList from '../../../components/tags/TagsList';
-import { getProjectUrl } from '../../../helpers/urls';
-import { isLoggedIn } from '../../../helpers/users';
-import { Project } from '../types';
-import ProjectCardOrganizationContainer from './ProjectCardOrganizationContainer';
-import ProjectCardOverallMeasures from './ProjectCardOverallMeasures';
-import ProjectCardQualityGate from './ProjectCardQualityGate';
-
-interface Props {
- currentUser: T.CurrentUser;
- handleFavorite: (component: string, isFavorite: boolean) => void;
- height: number;
- organization: T.Organization | undefined;
- project: Project;
-}
-
-export default class ProjectCardOverall extends React.PureComponent<Props> {
- render() {
- const { currentUser, handleFavorite, height, organization, project } = this.props;
- const { measures } = project;
-
- const hasTags = project.tags.length > 0;
-
- return (
- <div className="boxed-group project-card" data-key={project.key} style={{ height }}>
- <div className="boxed-group-header clearfix">
- <div className="project-card-header">
- {project.isFavorite !== undefined && (
- <Favorite
- className="spacer-right"
- component={project.key}
- favorite={project.isFavorite}
- handleFavorite={handleFavorite}
- qualifier="TRK"
- />
- )}
- <h2 className="project-card-name">
- {!organization && (
- <ProjectCardOrganizationContainer organization={project.organization} />
- )}
- <Link to={getProjectUrl(project.key)}>{project.name}</Link>
- </h2>
- {project.analysisDate && <ProjectCardQualityGate status={measures['alert_status']} />}
- <div className="project-card-header-right">
- <PrivacyBadgeContainer
- className="spacer-left"
- organization={organization || project.organization}
- qualifier="TRK"
- tooltipProps={{ projectKey: project.key }}
- visibility={project.visibility}
- />
- {hasTags && <TagsList className="spacer-left note" tags={project.tags} />}
- </div>
- </div>
- {project.analysisDate && (
- <div className="project-card-dates note text-right">
- <DateTimeFormatter date={project.analysisDate}>
- {formattedDate => (
- <span className="big-spacer-left">
- {translateWithParameters('projects.last_analysis_on_x', formattedDate)}
- </span>
- )}
- </DateTimeFormatter>
- </div>
- )}
- </div>
-
- {project.analysisDate ? (
- <div className="boxed-group-inner">
- {<ProjectCardOverallMeasures measures={measures} />}
- </div>
- ) : (
- <div className="boxed-group-inner">
- <div className="project-card-not-analyzed">
- <span className="note">{translate('projects.not_analyzed')}</span>
- {isLoggedIn(currentUser) && (
- <Link className="button spacer-left" to={getProjectUrl(project.key)}>
- {translate('projects.configure_analysis')}
- </Link>
- )}
- </div>
- </div>
- )}
- </div>
- );
- }
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.tsx b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.tsx
index 9198fd8f42c..93ee98af1e6 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.tsx
@@ -23,21 +23,29 @@ import SizeRating from 'sonar-ui-common/components/ui/SizeRating';
import { translate } from 'sonar-ui-common/helpers/l10n';
import Measure from '../../../components/measure/Measure';
import CoverageRating from '../../../components/ui/CoverageRating';
+import { ComponentQualifier } from '../../../types/component';
import ProjectCardLanguagesContainer from './ProjectCardLanguagesContainer';
import ProjectCardRatingMeasure from './ProjectCardRatingMeasure';
interface Props {
+ componentQualifier: ComponentQualifier;
measures: T.Dict<string | undefined>;
}
-export default function ProjectCardOverallMeasures({ measures }: Props) {
+export default function ProjectCardOverallMeasures({ componentQualifier, measures }: Props) {
if (measures === undefined) {
return null;
}
const { ncloc } = measures;
if (!ncloc) {
- return <div className="note">{translate('overview.project.main_branch_empty')}</div>;
+ return (
+ <div className="note big-spacer-top">
+ {componentQualifier === ComponentQualifier.Application
+ ? translate('portfolio.app.empty')
+ : translate('overview.project.main_branch_empty')}
+ </div>
+ );
}
return (
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx
index 794c7afc07f..3add3f6bbcb 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/AllProjects-test.tsx
@@ -22,6 +22,7 @@ import * as React from 'react';
import { get, save } from 'sonar-ui-common/helpers/storage';
import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
import { isSonarCloud } from '../../../../helpers/system';
+import { ComponentQualifier } from '../../../../types/component';
import { AllProjects } from '../AllProjects';
jest.mock('../ProjectsList', () => ({
@@ -212,7 +213,16 @@ function shallowRender(
);
wrapper.setState({
loading: false,
- projects: [{ key: 'foo', measures: {}, name: 'Foo', tags: [], visibility: 'public' }],
+ projects: [
+ {
+ key: 'foo',
+ measures: {},
+ name: 'Foo',
+ qualifier: ComponentQualifier.Project,
+ tags: [],
+ visibility: 'public'
+ }
+ ],
total: 0
});
return wrapper;
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCard-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCard-test.tsx
index 0fcbf20e45d..7b74ad36e93 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCard-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCard-test.tsx
@@ -19,11 +19,15 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import { mockCurrentUser } from '../../../../helpers/testMocks';
+import { mockCurrentUser, mockLoggedInUser } from '../../../../helpers/testMocks';
+import { ComponentQualifier } from '../../../../types/component';
import { Project } from '../../types';
import ProjectCard from '../ProjectCard';
-const ORGANIZATION = { key: 'org', name: 'org' };
+jest.mock(
+ 'date-fns/difference_in_milliseconds',
+ () => () => 1000 * 60 * 60 * 24 * 30 * 8 // ~ 8 months
+);
const MEASURES = {
alert_status: 'OK',
@@ -34,36 +38,91 @@ const MEASURES = {
const PROJECT: Project = {
analysisDate: '2017-01-01',
- leakPeriodDate: '2016-12-01',
key: 'foo',
measures: MEASURES,
name: 'Foo',
organization: { key: 'org', name: 'org' },
+ qualifier: ComponentQualifier.Project,
tags: [],
visibility: 'public'
};
-it('should show <ProjectCardOverall/> by default', () => {
- const wrapper = shallowRender();
- expect(wrapper.find('ProjectCardOverall').exists()).toBe(true);
- expect(wrapper.find('ProjectCardLeak').exists()).toBe(false);
+const USER_LOGGED_OUT = mockCurrentUser();
+const USER_LOGGED_IN = mockLoggedInUser();
+
+it('should display analysis date (and not leak period) when defined', () => {
+ expect(
+ shallowRender(PROJECT)
+ .find('.project-card-dates')
+ .exists()
+ ).toBe(true);
+ expect(
+ shallowRender({ ...PROJECT, analysisDate: undefined })
+ .find('.project-card-dates')
+ .exists()
+ ).toBe(false);
+});
+
+it('should not display the quality gate', () => {
+ const project = { ...PROJECT, analysisDate: undefined };
+ expect(
+ shallowRender(project)
+ .find('ProjectCardOverallQualityGate')
+ .exists()
+ ).toBe(false);
+});
+
+it('should display tags', () => {
+ const project = { ...PROJECT, tags: ['foo', 'bar'] };
+ expect(
+ shallowRender(project)
+ .find('TagsList')
+ .exists()
+ ).toBe(true);
+});
+
+it('should display private badge', () => {
+ const project: Project = { ...PROJECT, visibility: 'private' };
+ expect(
+ shallowRender(project)
+ .find('Connect(PrivacyBadge)')
+ .exists()
+ ).toBe(true);
+});
+
+it('should display the overall measures and quality gate', () => {
+ expect(shallowRender(PROJECT)).toMatchSnapshot();
+});
+
+it('should display not analyzed yet', () => {
+ expect(shallowRender({ ...PROJECT, analysisDate: undefined })).toMatchSnapshot();
+});
+
+it('should display configure analysis button for logged in user', () => {
+ expect(shallowRender({ ...PROJECT, analysisDate: undefined }, USER_LOGGED_IN)).toMatchSnapshot();
});
-it('should show <ProjectCardLeak/> when asked', () => {
- const wrapper = shallowRender('leak');
- expect(wrapper.find('ProjectCardLeak').exists()).toBe(true);
- expect(wrapper.find('ProjectCardOverall').exists()).toBe(false);
+it('should display applications', () => {
+ expect(
+ shallowRender({ ...PROJECT, qualifier: ComponentQualifier.Application })
+ ).toMatchSnapshot();
+ expect(
+ shallowRender({
+ ...PROJECT,
+ qualifier: ComponentQualifier.Application,
+ measures: { ...MEASURES, projects: '3' }
+ })
+ ).toMatchSnapshot('with project count');
});
-function shallowRender(type?: string) {
+function shallowRender(project: Project, user: T.CurrentUser = USER_LOGGED_OUT) {
return shallow(
<ProjectCard
- currentUser={mockCurrentUser()}
- handleFavorite={jest.fn}
- height={200}
- organization={ORGANIZATION}
- project={PROJECT}
- type={type}
+ currentUser={user}
+ handleFavorite={jest.fn()}
+ height={100}
+ organization={undefined}
+ project={project}
/>
);
}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeak-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeak-test.tsx
deleted file mode 100644
index ee5e548d38f..00000000000
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardLeak-test.tsx
+++ /dev/null
@@ -1,105 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockCurrentUser, mockLoggedInUser } from '../../../../helpers/testMocks';
-import { Project } from '../../types';
-import ProjectCardLeak from '../ProjectCardLeak';
-
-jest.mock(
- 'date-fns/difference_in_milliseconds',
- () => () => 1000 * 60 * 60 * 24 * 30 * 8 // ~ 8 months
-);
-
-const MEASURES = {
- alert_status: 'OK',
- reliability_rating: '1.0',
- sqale_rating: '1.0',
- new_bugs: '12'
-};
-
-const PROJECT: Project = {
- analysisDate: '2017-01-01',
- leakPeriodDate: '2016-12-01',
- key: 'foo',
- measures: MEASURES,
- name: 'Foo',
- organization: { key: 'org', name: 'org' },
- tags: [],
- visibility: 'public'
-};
-
-const USER_LOGGED_OUT = mockCurrentUser();
-const USER_LOGGED_IN = mockLoggedInUser();
-
-it('should display analysis date and leak start date', () => {
- const card = shallowRender(PROJECT);
- expect(card.find('.project-card-dates').exists()).toBe(true);
- expect(card.find('.project-card-dates').find('.project-card-leak-date')).toHaveLength(1);
- expect(card.find('.project-card-dates').find('DateTimeFormatter')).toHaveLength(1);
-});
-
-it('should not display analysis date or leak start date', () => {
- const project = { ...PROJECT, analysisDate: undefined };
- const card = shallowRender(project);
- expect(card.find('.project-card-dates').exists()).toBe(false);
-});
-
-it('should display tags', () => {
- const project = { ...PROJECT, tags: ['foo', 'bar'] };
- expect(
- shallowRender(project)
- .find('TagsList')
- .exists()
- ).toBe(true);
-});
-
-it('should display private badge', () => {
- const project: Project = { ...PROJECT, visibility: 'private' };
- expect(
- shallowRender(project)
- .find('Connect(PrivacyBadge)')
- .exists()
- ).toBe(true);
-});
-
-it('should display the leak measures and quality gate', () => {
- expect(shallowRender(PROJECT)).toMatchSnapshot();
-});
-
-it('should display not analyzed yet', () => {
- expect(shallowRender({ ...PROJECT, analysisDate: undefined })).toMatchSnapshot();
-});
-
-it('should display configure analysis button for logged in user', () => {
- expect(shallowRender({ ...PROJECT, analysisDate: undefined }, USER_LOGGED_IN)).toMatchSnapshot();
-});
-
-function shallowRender(project: Project, user: T.CurrentUser = USER_LOGGED_OUT) {
- return shallow(
- <ProjectCardLeak
- currentUser={user}
- handleFavorite={jest.fn()}
- height={100}
- organization={undefined}
- project={project}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverall-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverall-test.tsx
deleted file mode 100644
index 369c72ba783..00000000000
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverall-test.tsx
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2020 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 { shallow } from 'enzyme';
-import * as React from 'react';
-import { mockCurrentUser, mockLoggedInUser } from '../../../../helpers/testMocks';
-import { Project } from '../../types';
-import ProjectCardOverall from '../ProjectCardOverall';
-
-const MEASURES = {
- alert_status: 'OK',
- reliability_rating: '1.0',
- sqale_rating: '1.0',
- new_bugs: '12'
-};
-
-const PROJECT: Project = {
- analysisDate: '2017-01-01',
- key: 'foo',
- measures: MEASURES,
- name: 'Foo',
- organization: { key: 'org', name: 'org' },
- tags: [],
- visibility: 'public'
-};
-
-const USER_LOGGED_OUT = mockCurrentUser();
-const USER_LOGGED_IN = mockLoggedInUser();
-
-it('should display analysis date (and not leak period) when defined', () => {
- expect(
- shallowRender(PROJECT)
- .find('.project-card-dates')
- .exists()
- ).toBe(true);
- expect(
- shallowRender({ ...PROJECT, analysisDate: undefined })
- .find('.project-card-dates')
- .exists()
- ).toBe(false);
-});
-
-it('should not display the quality gate', () => {
- const project = { ...PROJECT, analysisDate: undefined };
- expect(
- shallowRender(project)
- .find('ProjectCardOverallQualityGate')
- .exists()
- ).toBe(false);
-});
-
-it('should display tags', () => {
- const project = { ...PROJECT, tags: ['foo', 'bar'] };
- expect(
- shallowRender(project)
- .find('TagsList')
- .exists()
- ).toBe(true);
-});
-
-it('should display private badge', () => {
- const project: Project = { ...PROJECT, visibility: 'private' };
- expect(
- shallowRender(project)
- .find('Connect(PrivacyBadge)')
- .exists()
- ).toBe(true);
-});
-
-it('should display the overall measures and quality gate', () => {
- expect(shallowRender(PROJECT)).toMatchSnapshot();
-});
-
-it('should display not analyzed yet', () => {
- expect(shallowRender({ ...PROJECT, analysisDate: undefined })).toMatchSnapshot();
-});
-
-it('should display configure analysis button for logged in user', () => {
- expect(shallowRender({ ...PROJECT, analysisDate: undefined }, USER_LOGGED_IN)).toMatchSnapshot();
-});
-
-function shallowRender(project: Project, user: T.CurrentUser = USER_LOGGED_OUT) {
- return shallow(
- <ProjectCardOverall
- currentUser={user}
- handleFavorite={jest.fn()}
- height={100}
- organization={undefined}
- project={project}
- />
- );
-}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverallMeasures-test.tsx b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverallMeasures-test.tsx
index 736a00ca4ef..4516ca937b6 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverallMeasures-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCardOverallMeasures-test.tsx
@@ -19,69 +19,53 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
+import { ComponentQualifier } from '../../../../types/component';
import ProjectCardOverallMeasures from '../ProjectCardOverallMeasures';
it('should render correctly with all data', () => {
- const measures = {
- alert_status: 'ERROR',
- bugs: '17',
- code_smells: '132',
- coverage: '88.3',
- duplicated_lines_density: '9.8',
- ncloc: '2053',
- reliability_rating: '1.0',
- security_rating: '1.0',
- sqale_rating: '1.0',
- vulnerabilities: '0'
- };
- const wrapper = shallow(<ProjectCardOverallMeasures measures={measures} />);
- expect(wrapper).toMatchSnapshot();
+ expect(shallowRender()).toMatchSnapshot();
});
it('should not render coverage', () => {
- const measures = {
- alert_status: 'ERROR',
- bugs: '17',
- code_smells: '132',
- duplicated_lines_density: '9.8',
- ncloc: '2053',
- reliability_rating: '1.0',
- security_rating: '1.0',
- sqale_rating: '1.0',
- vulnerabilities: '0'
- };
- const wrapper = shallow(<ProjectCardOverallMeasures measures={measures} />);
- expect(wrapper.find('[data-key="coverage"]').exists()).toBe(false);
+ expect(
+ shallowRender({ coverage: undefined })
+ .find('[data-key="coverage"]')
+ .exists()
+ ).toBe(false);
});
it('should render empty', () => {
- const measures = {
- alert_status: 'ERROR',
- bugs: '17',
- code_smells: '132',
- coverage: '88.3',
- duplicated_lines_density: '9.8',
- reliability_rating: '1.0',
- security_rating: '1.0',
- sqale_rating: '1.0',
- vulnerabilities: '0'
- };
- expect(shallow(<ProjectCardOverallMeasures measures={measures} />)).toMatchSnapshot();
+ expect(shallowRender({ ncloc: undefined })).toMatchSnapshot('project');
+ expect(shallowRender({ ncloc: undefined }, ComponentQualifier.Application)).toMatchSnapshot(
+ 'application'
+ );
});
it('should render ncloc correctly', () => {
+ expect(shallowRender({ ncloc: '16549887' }).find('[data-key="ncloc"]')).toMatchSnapshot();
+});
+
+function shallowRender(
+ overriddenMeasures: T.Dict<string | undefined> = {},
+ componentQualifier?: ComponentQualifier
+) {
const measures = {
alert_status: 'ERROR',
bugs: '17',
code_smells: '132',
coverage: '88.3',
- ncloc: '16549887',
duplicated_lines_density: '9.8',
+ ncloc: '2053',
reliability_rating: '1.0',
security_rating: '1.0',
sqale_rating: '1.0',
- vulnerabilities: '0'
+ vulnerabilities: '0',
+ ...overriddenMeasures
};
- const wrapper = shallow(<ProjectCardOverallMeasures measures={measures} />);
- expect(wrapper.find('[data-key="ncloc"]')).toMatchSnapshot();
-});
+ return shallow(
+ <ProjectCardOverallMeasures
+ componentQualifier={componentQualifier ?? ComponentQualifier.Project}
+ measures={measures}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap
index ecffd54942a..58f1e3f2750 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/AllProjects-test.tsx.snap
@@ -6,6 +6,7 @@ Array [
"key": "foo",
"measures": Object {},
"name": "Foo",
+ "qualifier": "TRK",
"tags": Array [],
"visibility": "public",
},
@@ -19,6 +20,7 @@ Array [
"key": "foo",
"measures": Object {},
"name": "Foo",
+ "qualifier": "TRK",
"tags": Array [],
"visibility": "public",
},
@@ -75,6 +77,7 @@ exports[`renders 1`] = `
"key": "foo",
"measures": Object {},
"name": "Foo",
+ "qualifier": "TRK",
"tags": Array [],
"visibility": "public",
},
@@ -132,6 +135,7 @@ exports[`renders 1`] = `
"key": "foo",
"measures": Object {},
"name": "Foo",
+ "qualifier": "TRK",
"tags": Array [],
"visibility": "public",
},
@@ -225,6 +229,7 @@ exports[`renders 2`] = `
"key": "foo",
"measures": Object {},
"name": "Foo",
+ "qualifier": "TRK",
"tags": Array [],
"visibility": "public",
},
@@ -254,6 +259,7 @@ exports[`renders 2`] = `
"key": "foo",
"measures": Object {},
"name": "Foo",
+ "qualifier": "TRK",
"tags": Array [],
"visibility": "public",
},
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCard-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCard-test.tsx.snap
new file mode 100644
index 00000000000..9232fe2c56c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCard-test.tsx.snap
@@ -0,0 +1,455 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should display applications 1`] = `
+<div
+ className="boxed-group project-card big-padded display-flex-column display-flex-space-between"
+ data-key="foo"
+ style={
+ Object {
+ "height": 100,
+ }
+ }
+>
+ <div>
+ <div
+ className="project-card-header"
+ >
+ <h2
+ className="project-card-name"
+ >
+ <Connect(ProjectCardOrganization)
+ organization={
+ Object {
+ "key": "org",
+ "name": "org",
+ }
+ }
+ />
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/dashboard",
+ "query": Object {
+ "branch": undefined,
+ "id": "foo",
+ },
+ }
+ }
+ >
+ Foo
+ </Link>
+ </h2>
+ <ProjectCardQualityGate
+ status="OK"
+ />
+ <div
+ className="project-card-header-right"
+ >
+ <Connect(PrivacyBadge)
+ className="spacer-left"
+ organization={
+ Object {
+ "key": "org",
+ "name": "org",
+ }
+ }
+ qualifier="APP"
+ tooltipProps={
+ Object {
+ "projectKey": "foo",
+ }
+ }
+ visibility="public"
+ />
+ </div>
+ </div>
+ <div
+ className="display-flex-center project-card-dates spacer-top"
+ >
+ <DateTimeFormatter
+ date="2017-01-01"
+ >
+ <Component />
+ </DateTimeFormatter>
+ <div
+ className="text-right flex-1-0-auto"
+ >
+ <QualifierIcon
+ className="spacer-right"
+ qualifier="APP"
+ />
+ qualifier.APP
+ </div>
+ </div>
+ </div>
+ <ProjectCardOverallMeasures
+ componentQualifier="APP"
+ measures={
+ Object {
+ "alert_status": "OK",
+ "new_bugs": "12",
+ "reliability_rating": "1.0",
+ "sqale_rating": "1.0",
+ }
+ }
+ />
+</div>
+`;
+
+exports[`should display applications: with project count 1`] = `
+<div
+ className="boxed-group project-card big-padded display-flex-column display-flex-space-between"
+ data-key="foo"
+ style={
+ Object {
+ "height": 100,
+ }
+ }
+>
+ <div>
+ <div
+ className="project-card-header"
+ >
+ <h2
+ className="project-card-name"
+ >
+ <Connect(ProjectCardOrganization)
+ organization={
+ Object {
+ "key": "org",
+ "name": "org",
+ }
+ }
+ />
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/dashboard",
+ "query": Object {
+ "branch": undefined,
+ "id": "foo",
+ },
+ }
+ }
+ >
+ Foo
+ </Link>
+ </h2>
+ <ProjectCardQualityGate
+ status="OK"
+ />
+ <div
+ className="project-card-header-right"
+ >
+ <Connect(PrivacyBadge)
+ className="spacer-left"
+ organization={
+ Object {
+ "key": "org",
+ "name": "org",
+ }
+ }
+ qualifier="APP"
+ tooltipProps={
+ Object {
+ "projectKey": "foo",
+ }
+ }
+ visibility="public"
+ />
+ </div>
+ </div>
+ <div
+ className="display-flex-center project-card-dates spacer-top"
+ >
+ <DateTimeFormatter
+ date="2017-01-01"
+ >
+ <Component />
+ </DateTimeFormatter>
+ <div
+ className="text-right flex-1-0-auto"
+ >
+ <QualifierIcon
+ className="spacer-right"
+ qualifier="APP"
+ />
+ qualifier.APP
+ ‒
+ x_projects_.3
+ </div>
+ </div>
+ </div>
+ <ProjectCardOverallMeasures
+ componentQualifier="APP"
+ measures={
+ Object {
+ "alert_status": "OK",
+ "new_bugs": "12",
+ "projects": "3",
+ "reliability_rating": "1.0",
+ "sqale_rating": "1.0",
+ }
+ }
+ />
+</div>
+`;
+
+exports[`should display configure analysis button for logged in user 1`] = `
+<div
+ className="boxed-group project-card big-padded display-flex-column display-flex-space-between"
+ data-key="foo"
+ style={
+ Object {
+ "height": 100,
+ }
+ }
+>
+ <div>
+ <div
+ className="project-card-header"
+ >
+ <h2
+ className="project-card-name"
+ >
+ <Connect(ProjectCardOrganization)
+ organization={
+ Object {
+ "key": "org",
+ "name": "org",
+ }
+ }
+ />
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/dashboard",
+ "query": Object {
+ "branch": undefined,
+ "id": "foo",
+ },
+ }
+ }
+ >
+ Foo
+ </Link>
+ </h2>
+ <div
+ className="project-card-header-right"
+ >
+ <Connect(PrivacyBadge)
+ className="spacer-left"
+ organization={
+ Object {
+ "key": "org",
+ "name": "org",
+ }
+ }
+ qualifier="TRK"
+ tooltipProps={
+ Object {
+ "projectKey": "foo",
+ }
+ }
+ visibility="public"
+ />
+ </div>
+ </div>
+ </div>
+ <div
+ className="project-card-not-analyzed"
+ >
+ <span
+ className="note"
+ >
+ projects.not_analyzed.TRK
+ </span>
+ <Link
+ className="button spacer-left"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/dashboard",
+ "query": Object {
+ "branch": undefined,
+ "id": "foo",
+ },
+ }
+ }
+ >
+ projects.configure_analysis
+ </Link>
+ </div>
+</div>
+`;
+
+exports[`should display not analyzed yet 1`] = `
+<div
+ className="boxed-group project-card big-padded display-flex-column display-flex-space-between"
+ data-key="foo"
+ style={
+ Object {
+ "height": 100,
+ }
+ }
+>
+ <div>
+ <div
+ className="project-card-header"
+ >
+ <h2
+ className="project-card-name"
+ >
+ <Connect(ProjectCardOrganization)
+ organization={
+ Object {
+ "key": "org",
+ "name": "org",
+ }
+ }
+ />
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/dashboard",
+ "query": Object {
+ "branch": undefined,
+ "id": "foo",
+ },
+ }
+ }
+ >
+ Foo
+ </Link>
+ </h2>
+ <div
+ className="project-card-header-right"
+ >
+ <Connect(PrivacyBadge)
+ className="spacer-left"
+ organization={
+ Object {
+ "key": "org",
+ "name": "org",
+ }
+ }
+ qualifier="TRK"
+ tooltipProps={
+ Object {
+ "projectKey": "foo",
+ }
+ }
+ visibility="public"
+ />
+ </div>
+ </div>
+ </div>
+ <div
+ className="project-card-not-analyzed"
+ >
+ <span
+ className="note"
+ >
+ projects.not_analyzed.TRK
+ </span>
+ </div>
+</div>
+`;
+
+exports[`should display the overall measures and quality gate 1`] = `
+<div
+ className="boxed-group project-card big-padded display-flex-column display-flex-space-between"
+ data-key="foo"
+ style={
+ Object {
+ "height": 100,
+ }
+ }
+>
+ <div>
+ <div
+ className="project-card-header"
+ >
+ <h2
+ className="project-card-name"
+ >
+ <Connect(ProjectCardOrganization)
+ organization={
+ Object {
+ "key": "org",
+ "name": "org",
+ }
+ }
+ />
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/dashboard",
+ "query": Object {
+ "branch": undefined,
+ "id": "foo",
+ },
+ }
+ }
+ >
+ Foo
+ </Link>
+ </h2>
+ <ProjectCardQualityGate
+ status="OK"
+ />
+ <div
+ className="project-card-header-right"
+ >
+ <Connect(PrivacyBadge)
+ className="spacer-left"
+ organization={
+ Object {
+ "key": "org",
+ "name": "org",
+ }
+ }
+ qualifier="TRK"
+ tooltipProps={
+ Object {
+ "projectKey": "foo",
+ }
+ }
+ visibility="public"
+ />
+ </div>
+ </div>
+ <div
+ className="display-flex-center project-card-dates spacer-top"
+ >
+ <DateTimeFormatter
+ date="2017-01-01"
+ >
+ <Component />
+ </DateTimeFormatter>
+ </div>
+ </div>
+ <ProjectCardOverallMeasures
+ componentQualifier="TRK"
+ measures={
+ Object {
+ "alert_status": "OK",
+ "new_bugs": "12",
+ "reliability_rating": "1.0",
+ "sqale_rating": "1.0",
+ }
+ }
+ />
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeak-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeak-test.tsx.snap
deleted file mode 100644
index fb8431450f3..00000000000
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardLeak-test.tsx.snap
+++ /dev/null
@@ -1,275 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display configure analysis button for logged in user 1`] = `
-<div
- className="boxed-group project-card"
- data-key="foo"
- style={
- Object {
- "height": 100,
- }
- }
->
- <div
- className="boxed-group-header clearfix"
- >
- <div
- className="project-card-header"
- >
- <h2
- className="project-card-name"
- >
- <Connect(ProjectCardOrganization)
- organization={
- Object {
- "key": "org",
- "name": "org",
- }
- }
- />
- <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/dashboard",
- "query": Object {
- "id": "foo",
- },
- }
- }
- >
- Foo
- </Link>
- </h2>
- <div
- className="project-card-header-right"
- >
- <Connect(PrivacyBadge)
- className="spacer-left"
- organization={
- Object {
- "key": "org",
- "name": "org",
- }
- }
- qualifier="TRK"
- tooltipProps={
- Object {
- "projectKey": "foo",
- }
- }
- visibility="public"
- />
- </div>
- </div>
- </div>
- <div
- className="boxed-group-inner"
- >
- <div
- className="project-card-not-analyzed"
- >
- <span
- className="note"
- >
- projects.not_analyzed
- </span>
- <Link
- className="button spacer-left"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/dashboard",
- "query": Object {
- "branch": undefined,
- "id": "foo",
- },
- }
- }
- >
- projects.configure_analysis
- </Link>
- </div>
- </div>
-</div>
-`;
-
-exports[`should display not analyzed yet 1`] = `
-<div
- className="boxed-group project-card"
- data-key="foo"
- style={
- Object {
- "height": 100,
- }
- }
->
- <div
- className="boxed-group-header clearfix"
- >
- <div
- className="project-card-header"
- >
- <h2
- className="project-card-name"
- >
- <Connect(ProjectCardOrganization)
- organization={
- Object {
- "key": "org",
- "name": "org",
- }
- }
- />
- <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/dashboard",
- "query": Object {
- "id": "foo",
- },
- }
- }
- >
- Foo
- </Link>
- </h2>
- <div
- className="project-card-header-right"
- >
- <Connect(PrivacyBadge)
- className="spacer-left"
- organization={
- Object {
- "key": "org",
- "name": "org",
- }
- }
- qualifier="TRK"
- tooltipProps={
- Object {
- "projectKey": "foo",
- }
- }
- visibility="public"
- />
- </div>
- </div>
- </div>
- <div
- className="boxed-group-inner"
- >
- <div
- className="project-card-not-analyzed"
- >
- <span
- className="note"
- >
- projects.not_analyzed
- </span>
- </div>
- </div>
-</div>
-`;
-
-exports[`should display the leak measures and quality gate 1`] = `
-<div
- className="boxed-group project-card"
- data-key="foo"
- style={
- Object {
- "height": 100,
- }
- }
->
- <div
- className="boxed-group-header clearfix"
- >
- <div
- className="project-card-header"
- >
- <h2
- className="project-card-name"
- >
- <Connect(ProjectCardOrganization)
- organization={
- Object {
- "key": "org",
- "name": "org",
- }
- }
- />
- <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/dashboard",
- "query": Object {
- "id": "foo",
- },
- }
- }
- >
- Foo
- </Link>
- </h2>
- <ProjectCardQualityGate
- status="OK"
- />
- <div
- className="project-card-header-right"
- >
- <Connect(PrivacyBadge)
- className="spacer-left"
- organization={
- Object {
- "key": "org",
- "name": "org",
- }
- }
- qualifier="TRK"
- tooltipProps={
- Object {
- "projectKey": "foo",
- }
- }
- visibility="public"
- />
- </div>
- </div>
- <div
- className="project-card-dates note text-right pull-right"
- >
- <span
- className="project-card-leak-date pull-right"
- >
- projects.new_code_period_x.duration.months.8
- </span>
- <DateTimeFormatter
- date="2017-01-01"
- >
- <Component />
- </DateTimeFormatter>
- </div>
- </div>
- <div
- className="boxed-group-inner"
- >
- <ProjectCardLeakMeasures
- measures={
- Object {
- "alert_status": "OK",
- "new_bugs": "12",
- "reliability_rating": "1.0",
- "sqale_rating": "1.0",
- }
- }
- />
- </div>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverall-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverall-test.tsx.snap
deleted file mode 100644
index 0c70453a77f..00000000000
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverall-test.tsx.snap
+++ /dev/null
@@ -1,273 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should display configure analysis button for logged in user 1`] = `
-<div
- className="boxed-group project-card"
- data-key="foo"
- style={
- Object {
- "height": 100,
- }
- }
->
- <div
- className="boxed-group-header clearfix"
- >
- <div
- className="project-card-header"
- >
- <h2
- className="project-card-name"
- >
- <Connect(ProjectCardOrganization)
- organization={
- Object {
- "key": "org",
- "name": "org",
- }
- }
- />
- <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/dashboard",
- "query": Object {
- "branch": undefined,
- "id": "foo",
- },
- }
- }
- >
- Foo
- </Link>
- </h2>
- <div
- className="project-card-header-right"
- >
- <Connect(PrivacyBadge)
- className="spacer-left"
- organization={
- Object {
- "key": "org",
- "name": "org",
- }
- }
- qualifier="TRK"
- tooltipProps={
- Object {
- "projectKey": "foo",
- }
- }
- visibility="public"
- />
- </div>
- </div>
- </div>
- <div
- className="boxed-group-inner"
- >
- <div
- className="project-card-not-analyzed"
- >
- <span
- className="note"
- >
- projects.not_analyzed
- </span>
- <Link
- className="button spacer-left"
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/dashboard",
- "query": Object {
- "branch": undefined,
- "id": "foo",
- },
- }
- }
- >
- projects.configure_analysis
- </Link>
- </div>
- </div>
-</div>
-`;
-
-exports[`should display not analyzed yet 1`] = `
-<div
- className="boxed-group project-card"
- data-key="foo"
- style={
- Object {
- "height": 100,
- }
- }
->
- <div
- className="boxed-group-header clearfix"
- >
- <div
- className="project-card-header"
- >
- <h2
- className="project-card-name"
- >
- <Connect(ProjectCardOrganization)
- organization={
- Object {
- "key": "org",
- "name": "org",
- }
- }
- />
- <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/dashboard",
- "query": Object {
- "branch": undefined,
- "id": "foo",
- },
- }
- }
- >
- Foo
- </Link>
- </h2>
- <div
- className="project-card-header-right"
- >
- <Connect(PrivacyBadge)
- className="spacer-left"
- organization={
- Object {
- "key": "org",
- "name": "org",
- }
- }
- qualifier="TRK"
- tooltipProps={
- Object {
- "projectKey": "foo",
- }
- }
- visibility="public"
- />
- </div>
- </div>
- </div>
- <div
- className="boxed-group-inner"
- >
- <div
- className="project-card-not-analyzed"
- >
- <span
- className="note"
- >
- projects.not_analyzed
- </span>
- </div>
- </div>
-</div>
-`;
-
-exports[`should display the overall measures and quality gate 1`] = `
-<div
- className="boxed-group project-card"
- data-key="foo"
- style={
- Object {
- "height": 100,
- }
- }
->
- <div
- className="boxed-group-header clearfix"
- >
- <div
- className="project-card-header"
- >
- <h2
- className="project-card-name"
- >
- <Connect(ProjectCardOrganization)
- organization={
- Object {
- "key": "org",
- "name": "org",
- }
- }
- />
- <Link
- onlyActiveOnIndex={false}
- style={Object {}}
- to={
- Object {
- "pathname": "/dashboard",
- "query": Object {
- "branch": undefined,
- "id": "foo",
- },
- }
- }
- >
- Foo
- </Link>
- </h2>
- <ProjectCardQualityGate
- status="OK"
- />
- <div
- className="project-card-header-right"
- >
- <Connect(PrivacyBadge)
- className="spacer-left"
- organization={
- Object {
- "key": "org",
- "name": "org",
- }
- }
- qualifier="TRK"
- tooltipProps={
- Object {
- "projectKey": "foo",
- }
- }
- visibility="public"
- />
- </div>
- </div>
- <div
- className="project-card-dates note text-right"
- >
- <DateTimeFormatter
- date="2017-01-01"
- >
- <Component />
- </DateTimeFormatter>
- </div>
- </div>
- <div
- className="boxed-group-inner"
- >
- <ProjectCardOverallMeasures
- measures={
- Object {
- "alert_status": "OK",
- "new_bugs": "12",
- "reliability_rating": "1.0",
- "sqale_rating": "1.0",
- }
- }
- />
- </div>
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.tsx.snap
index 23088af1a46..69a88c913e4 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCardOverallMeasures-test.tsx.snap
@@ -180,9 +180,17 @@ exports[`should render correctly with all data 1`] = `
</div>
`;
-exports[`should render empty 1`] = `
+exports[`should render empty: application 1`] = `
<div
- className="note"
+ className="note big-spacer-top"
+>
+ portfolio.app.empty
+</div>
+`;
+
+exports[`should render empty: project 1`] = `
+<div
+ className="note big-spacer-top"
>
overview.project.main_branch_empty
</div>
diff --git a/server/sonar-web/src/main/js/apps/projects/styles.css b/server/sonar-web/src/main/js/apps/projects/styles.css
index e263c6257b3..7cd2e2a6216 100644
--- a/server/sonar-web/src/main/js/apps/projects/styles.css
+++ b/server/sonar-web/src/main/js/apps/projects/styles.css
@@ -77,12 +77,6 @@
white-space: nowrap;
}
-.project-card-dates {
- width: 100%;
- margin-top: 10px;
- margin-bottom: -10px;
-}
-
.project-card-leak-date {
padding: 4px 8px;
margin: -5px -4px -5px 24px;
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 1bd13d97339..566e8496b1d 100644
--- a/server/sonar-web/src/main/js/apps/projects/types.ts
+++ b/server/sonar-web/src/main/js/apps/projects/types.ts
@@ -17,6 +17,8 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { ComponentQualifier } from '../../types/component';
+
export interface Project {
analysisDate?: string;
isFavorite?: boolean;
@@ -25,6 +27,8 @@ export interface Project {
measures: T.Dict<string>;
name: string;
organization?: { key: string; name: string };
+ projects?: number;
+ qualifier: ComponentQualifier;
tags: string[];
visibility: T.Visibility;
}
diff --git a/server/sonar-web/src/main/js/apps/projects/utils.ts b/server/sonar-web/src/main/js/apps/projects/utils.ts
index 926d048366e..445c5119601 100644
--- a/server/sonar-web/src/main/js/apps/projects/utils.ts
+++ b/server/sonar-web/src/main/js/apps/projects/utils.ts
@@ -24,6 +24,7 @@ import { Facet, searchProjects } from '../../api/components';
import { getMeasuresForProjects } from '../../api/measures';
import { getOrganizations } from '../../api/organizations';
import { getPeriodValue, isDiffMetric } from '../../helpers/measures';
+import { MetricKey } from '../../types/metrics';
import { convertToFilter, Query } from './query';
interface SortingOption {
@@ -92,44 +93,67 @@ const PAGE_SIZE = 50;
const PAGE_SIZE_VISUALIZATIONS = 99;
const METRICS = [
- 'alert_status',
- 'bugs',
- 'reliability_rating',
- 'vulnerabilities',
- 'security_rating',
- 'security_hotspots_reviewed',
- 'security_review_rating',
- 'code_smells',
- 'sqale_rating',
- 'duplicated_lines_density',
- 'coverage',
- 'ncloc',
- 'ncloc_language_distribution'
+ MetricKey.alert_status,
+ MetricKey.bugs,
+ MetricKey.reliability_rating,
+ MetricKey.vulnerabilities,
+ MetricKey.security_rating,
+ MetricKey.security_hotspots_reviewed,
+ MetricKey.security_review_rating,
+ MetricKey.code_smells,
+ MetricKey.sqale_rating,
+ MetricKey.duplicated_lines_density,
+ MetricKey.coverage,
+ MetricKey.ncloc,
+ MetricKey.ncloc_language_distribution,
+ MetricKey.projects
];
const LEAK_METRICS = [
- 'alert_status',
- 'new_bugs',
- 'new_reliability_rating',
- 'new_vulnerabilities',
- 'new_security_rating',
- 'new_security_hotspots_reviewed',
- 'new_security_review_rating',
- 'new_code_smells',
- 'new_maintainability_rating',
- 'new_coverage',
- 'new_duplicated_lines_density',
- 'new_lines'
+ MetricKey.alert_status,
+ MetricKey.new_bugs,
+ MetricKey.new_reliability_rating,
+ MetricKey.new_vulnerabilities,
+ MetricKey.new_security_rating,
+ MetricKey.new_security_hotspots_reviewed,
+ MetricKey.new_security_review_rating,
+ MetricKey.new_code_smells,
+ MetricKey.new_maintainability_rating,
+ MetricKey.new_coverage,
+ MetricKey.new_duplicated_lines_density,
+ MetricKey.new_lines,
+ MetricKey.projects
];
const METRICS_BY_VISUALIZATION: T.Dict<string[]> = {
- risk: ['reliability_rating', 'security_rating', 'coverage', 'ncloc', 'sqale_index'],
+ risk: [
+ MetricKey.reliability_rating,
+ MetricKey.security_rating,
+ MetricKey.coverage,
+ MetricKey.ncloc,
+ MetricKey.sqale_index
+ ],
// x, y, size, color
- reliability: ['ncloc', 'reliability_remediation_effort', 'bugs', 'reliability_rating'],
- security: ['ncloc', 'security_remediation_effort', 'vulnerabilities', 'security_rating'],
- maintainability: ['ncloc', 'sqale_index', 'code_smells', 'sqale_rating'],
- coverage: ['complexity', 'coverage', 'uncovered_lines'],
- duplications: ['ncloc', 'duplicated_lines_density', 'duplicated_blocks']
+ reliability: [
+ MetricKey.ncloc,
+ MetricKey.reliability_remediation_effort,
+ MetricKey.bugs,
+ MetricKey.reliability_rating
+ ],
+ security: [
+ MetricKey.ncloc,
+ MetricKey.security_remediation_effort,
+ MetricKey.vulnerabilities,
+ MetricKey.security_rating
+ ],
+ maintainability: [
+ MetricKey.ncloc,
+ MetricKey.sqale_index,
+ MetricKey.code_smells,
+ MetricKey.sqale_rating
+ ],
+ coverage: [MetricKey.complexity, MetricKey.coverage, MetricKey.uncovered_lines],
+ duplications: [MetricKey.ncloc, MetricKey.duplicated_lines_density, MetricKey.duplicated_blocks]
};
export const FACETS = [
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/Risk.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/Risk.tsx
index 445e872382f..161202d0c6b 100644
--- a/server/sonar-web/src/main/js/apps/projects/visualizations/Risk.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/Risk.tsx
@@ -20,11 +20,13 @@
import * as React from 'react';
import BubbleChart from 'sonar-ui-common/components/charts/BubbleChart';
import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip';
+import QualifierIcon from 'sonar-ui-common/components/icons/QualifierIcon';
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
import { formatMeasure } from 'sonar-ui-common/helpers/measures';
import ColorRatingsLegend from '../../../components/charts/ColorRatingsLegend';
import { RATING_COLORS } from '../../../helpers/constants';
import { getProjectUrl } from '../../../helpers/urls';
+import { ComponentQualifier } from '../../../types/component';
import { Project } from '../types';
const X_METRIC = 'sqale_index';
@@ -77,7 +79,20 @@ export default class Risk extends React.PureComponent<Props> {
return (
<div className="text-left">
- <div className="little-spacer-bottom">{fullProjectName}</div>
+ <div className="little-spacer-bottom display-flex-center display-flex-space-between">
+ {fullProjectName}
+
+ {project.qualifier === ComponentQualifier.Application && (
+ <div className="big-spacer-left nowrap">
+ <QualifierIcon
+ className="little-spacer-right"
+ fill="currentColor"
+ qualifier={ComponentQualifier.Application}
+ />
+ {translate('qualifier.APP')}
+ </div>
+ )}
+ </div>
{this.getMetricTooltip({ key: COLOR_METRIC_1, type: COLOR_METRIC_TYPE }, color1)}
{this.getMetricTooltip({ key: COLOR_METRIC_2, type: COLOR_METRIC_TYPE }, color2)}
{this.getMetricTooltip({ key: Y_METRIC, type: Y_METRIC_TYPE }, y)}
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.tsx
index a4fb6562a0c..55f0ec925dd 100644
--- a/server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/SimpleBubbleChart.tsx
@@ -20,11 +20,13 @@
import * as React from 'react';
import BubbleChart from 'sonar-ui-common/components/charts/BubbleChart';
import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip';
+import QualifierIcon from 'sonar-ui-common/components/icons/QualifierIcon';
import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
import { formatMeasure } from 'sonar-ui-common/helpers/measures';
import ColorRatingsLegend from '../../../components/charts/ColorRatingsLegend';
import { RATING_COLORS } from '../../../helpers/constants';
import { getProjectUrl } from '../../../helpers/urls';
+import { ComponentQualifier } from '../../../types/component';
import { Project } from '../types';
interface Metric {
@@ -71,7 +73,20 @@ export default class SimpleBubbleChart extends React.PureComponent<Props> {
return (
<div className="text-left">
- <div className="little-spacer-bottom">{fullProjectName}</div>
+ <div className="little-spacer-bottom display-flex-center display-flex-space-between">
+ {fullProjectName}
+
+ {project.qualifier === ComponentQualifier.Application && (
+ <div className="big-spacer-left nowrap">
+ <QualifierIcon
+ className="little-spacer-right"
+ fill="currentColor"
+ qualifier={ComponentQualifier.Application}
+ />
+ {translate('qualifier.APP')}
+ </div>
+ )}
+ </div>
{this.getMetricTooltip(this.props.xMetric, x)}
{this.getMetricTooltip(this.props.yMetric, y)}
{this.getMetricTooltip(this.props.sizeMetric, size)}
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Risk-test.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Risk-test.tsx
index 07f7327c7eb..b212b3be785 100644
--- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Risk-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/Risk-test.tsx
@@ -19,6 +19,7 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
+import { ComponentQualifier } from '../../../../types/component';
import { Project } from '../../types';
import Risk from '../Risk';
@@ -27,6 +28,7 @@ it('renders', () => {
key: 'foo',
measures: { complexity: '17.2', coverage: '53.5', ncloc: '1734' },
name: 'Foo',
+ qualifier: ComponentQualifier.Project,
tags: [],
visibility: 'public'
};
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/SimpleBubbleChart-test.tsx b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/SimpleBubbleChart-test.tsx
index c8b76798e4f..00a64d12e9e 100644
--- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/SimpleBubbleChart-test.tsx
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/SimpleBubbleChart-test.tsx
@@ -19,6 +19,7 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
+import { ComponentQualifier } from '../../../../types/component';
import { Project } from '../../types';
import SimpleBubbleChart from '../SimpleBubbleChart';
@@ -27,16 +28,24 @@ it('renders', () => {
key: 'foo',
measures: { complexity: '17.2', coverage: '53.5', ncloc: '1734', security_rating: '2' },
name: 'Foo',
+ qualifier: ComponentQualifier.Project,
tags: [],
visibility: 'public'
};
+ const app = {
+ ...project1,
+ key: 'app',
+ measures: { complexity: '23.1', coverage: '87.3', ncloc: '32478', security_rating: '1' },
+ name: 'App',
+ qualifier: ComponentQualifier.Application
+ };
expect(
shallow(
<SimpleBubbleChart
colorMetric="security_rating"
displayOrganizations={false}
helpText="foobar"
- projects={[project1]}
+ projects={[app, project1]}
sizeMetric={{ key: 'ncloc', type: 'INT' }}
xMetric={{ key: 'complexity', type: 'INT' }}
yMetric={{ key: 'coverage', type: 'PERCENT' }}
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap
index 38408926a5b..381818cef6b 100644
--- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/Risk-test.tsx.snap
@@ -27,7 +27,7 @@ exports[`renders 1`] = `
className="text-left"
>
<div
- className="little-spacer-bottom"
+ className="little-spacer-bottom display-flex-center display-flex-space-between"
>
<strong>
Foo
diff --git a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap
index fd133225a39..7d0403e0300 100644
--- a/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/projects/visualizations/__tests__/__snapshots__/SimpleBubbleChart-test.tsx.snap
@@ -13,6 +13,61 @@ exports[`renders 1`] = `
items={
Array [
Object {
+ "color": "#00aa00",
+ "key": "app",
+ "link": Object {
+ "pathname": "/dashboard",
+ "query": Object {
+ "branch": undefined,
+ "id": "app",
+ },
+ },
+ "size": 32478,
+ "tooltip": <div
+ className="text-left"
+ >
+ <div
+ className="little-spacer-bottom display-flex-center display-flex-space-between"
+ >
+ <strong>
+ App
+ </strong>
+ <div
+ className="big-spacer-left nowrap"
+ >
+ <QualifierIcon
+ className="little-spacer-right"
+ fill="currentColor"
+ qualifier="APP"
+ />
+ qualifier.APP
+ </div>
+ </div>
+ <div>
+ metric.complexity.name
+ :
+ 23
+ </div>
+ <div>
+ metric.coverage.name
+ :
+ 87.3%
+ </div>
+ <div>
+ metric.ncloc.name
+ :
+ 32,478
+ </div>
+ <div>
+ metric.security_rating.name
+ :
+ A
+ </div>
+ </div>,
+ "x": 23.1,
+ "y": 87.3,
+ },
+ Object {
"color": "#b0d513",
"key": "foo",
"link": Object {
@@ -27,7 +82,7 @@ exports[`renders 1`] = `
className="text-left"
>
<div
- className="little-spacer-bottom"
+ className="little-spacer-bottom display-flex-center display-flex-space-between"
>
<strong>
Foo