aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web
diff options
context:
space:
mode:
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-05-24 15:06:17 +0200
committerGrégoire Aubert <gregoire.aubert@sonarsource.com>2017-06-09 08:26:48 +0200
commit2fb2871f4be5ea216ab71e67b80622c040fd1791 (patch)
treec4700ff5f5dbda793a6f7577925ba345abf37f19 /server/sonar-web
parent5ad1c512b2f8f3a07cfd72ca803c66d4c6c31988 (diff)
downloadsonarqube-2fb2871f4be5ea216ab71e67b80622c040fd1791.tar.gz
sonarqube-2fb2871f4be5ea216ab71e67b80622c040fd1791.zip
SONAR-9245 Add the leak period start date
Diffstat (limited to 'server/sonar-web')
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectCard.js49
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.js2
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCard-test.js131
-rw-r--r--server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCard-test.js.snap77
-rw-r--r--server/sonar-web/src/main/js/apps/projects/store/actions.js4
-rw-r--r--server/sonar-web/src/main/js/apps/projects/styles.css7
6 files changed, 199 insertions, 71 deletions
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCard.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectCard.js
index 18b485bdcc8..2150c92ba7d 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCard.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCard.js
@@ -37,10 +37,12 @@ type Props = {
project?: {
analysisDate?: string,
key: string,
+ leakPeriodDate?: string,
name: string,
tags: Array<string>,
isFavorite?: boolean,
- organization?: string
+ organization?: string,
+ visibility?: boolean
},
type?: string
};
@@ -51,6 +53,9 @@ export default function ProjectCard({ measures, organization, project, type }: P
}
const isProjectAnalyzed = project.analysisDate != null;
+ const isPrivate = project.visibility === 'private';
+ const hasLeakPeriodStart = project.leakPeriodDate != null;
+ const hasTags = project.tags.length > 0;
const isLeakView = type === 'leak';
let areProjectMeasuresLoaded;
@@ -72,22 +77,7 @@ export default function ProjectCard({ measures, organization, project, type }: P
return (
<div data-key={project.key} className={className}>
-
- <div className="boxed-group-actions text-right">
- {project.visibility === 'private' &&
- <PrivateBadge className="spacer-left" tooltipPlacement="left" />}
- {project.tags.length > 0 && <TagsList tags={project.tags} customClass="spacer-left" />}
- {isLeakView &&
- isProjectAnalyzed &&
- <div className="little-spacer-top spacer-left note">
- {translateWithParameters(
- 'overview.last_analysis_on_x',
- moment(project.analysisDate).format('LLL')
- )}
- </div>}
- </div>
-
- <div className="boxed-group-header">
+ <div className="boxed-group-header clearfix">
{project.isFavorite != null &&
<FavoriteContainer className="spacer-right" componentKey={project.key} />}
<h2 className="project-card-name">
@@ -99,6 +89,31 @@ export default function ProjectCard({ measures, organization, project, type }: P
<Link to={{ pathname: '/dashboard', query: { id: project.key } }}>{project.name}</Link>
</h2>
{displayQualityGate && <ProjectCardQualityGate status={measures['alert_status']} />}
+ <div className="pull-right text-right">
+ {isPrivate && <PrivateBadge className="spacer-left" tooltipPlacement="left" />}
+ {hasTags && <TagsList tags={project.tags} customClass="spacer-left" />}
+ </div>
+ {isLeakView &&
+ isProjectAnalyzed &&
+ <div
+ className={classNames('project-card-dates note text-right pull-right', {
+ 'width-100': isPrivate || hasTags
+ })}>
+ {hasLeakPeriodStart &&
+ <span>
+ {translateWithParameters(
+ 'projects.leak_period_x',
+ moment(project.leakPeriodDate).fromNow()
+ )}
+ </span>}
+ {isProjectAnalyzed &&
+ <span className="big-spacer-left">
+ {translateWithParameters(
+ 'projects.last_analysis_on_x',
+ moment(project.analysisDate).format('LLL')
+ )}
+ </span>}
+ </div>}
</div>
{isProjectAnalyzed
diff --git a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.js b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.js
index 87ad40ee285..219a6650903 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/ProjectCardOverallMeasures.js
@@ -31,7 +31,7 @@ type Props = {
measures?: { [string]: string }
};
-export default function ProjectCardMeasures({ measures }: Props) {
+export default function ProjectCardOverallMeasures({ measures }: Props) {
if (measures == null) {
return null;
}
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCard-test.js b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCard-test.js
index bb5b0ecef82..186b45b2737 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCard-test.js
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/ProjectCard-test.js
@@ -21,31 +21,116 @@ import React from 'react';
import { shallow } from 'enzyme';
import ProjectCard from '../ProjectCard';
-const PROJECT = { analysisDate: '2017-01-01', key: 'foo', name: 'Foo', tags: [] };
-const MEASURES = {};
-
-it('should not display analysis date', () => {
- expect(
- shallow(<ProjectCard measures={MEASURES} project={PROJECT} />).find(
- '.project-card-analysis-date'
- )
- ).toMatchSnapshot();
-});
+const PROJECT = {
+ analysisDate: '2017-01-01',
+ leakPeriodDate: '2016-12-01',
+ key: 'foo',
+ name: 'Foo',
+ tags: []
+};
+const MEASURES = {
+ alert_status: 'OK',
+ reliability_rating: '1.0',
+ sqale_rating: '1.0',
+ new_bugs: 12
+};
-it('should NOT display analysis date', () => {
- const project = { ...PROJECT, analysisDate: undefined };
- expect(
- shallow(<ProjectCard measures={MEASURES} project={project} />)
- .find('.project-card-analysis-date')
- .exists()
- ).toBeFalsy();
-});
+jest.mock('moment', () => () => ({
+ format: () => 'March 1, 2017 9:36 AM',
+ fromNow: () => 'a month ago'
+}));
+
+describe('overall status project card', () => {
+ it('should never display analysis date', () => {
+ expect(
+ shallow(<ProjectCard measures={{}} project={PROJECT} />).find('.project-card-dates').exists()
+ ).toBeFalsy();
+ });
+
+ it('should display loading', () => {
+ const measures = { ...MEASURES, sqale_rating: undefined };
+ expect(
+ shallow(<ProjectCard project={PROJECT} />)
+ .find('.boxed-group')
+ .hasClass('boxed-group-loading')
+ ).toBeTruthy();
+ expect(
+ shallow(<ProjectCard measures={measures} project={PROJECT} />)
+ .find('.boxed-group')
+ .hasClass('boxed-group-loading')
+ ).toBeTruthy();
+ });
+
+ it('should not display the quality gate', () => {
+ const project = { ...PROJECT, analysisDate: undefined };
+ expect(
+ shallow(<ProjectCard measures={MEASURES} project={project} />)
+ .find('ProjectCardQualityGate')
+ .exists()
+ ).toBeFalsy();
+ });
+
+ it('should display tags', () => {
+ const project = { ...PROJECT, tags: ['foo', 'bar'] };
+ expect(shallow(<ProjectCard project={project} />).find('TagsList').exists()).toBeTruthy();
+ });
-it('should display loading', () => {
- expect(shallow(<ProjectCard project={PROJECT} />)).toMatchSnapshot();
+ it('should private badge', () => {
+ const project = { ...PROJECT, visibility: 'private' };
+ expect(
+ shallow(<ProjectCard type="overall" project={project} />).find('PrivateBadge').exists()
+ ).toBeTruthy();
+ });
+
+ it('should display the overall measures and quality gate', () => {
+ expect(shallow(<ProjectCard measures={MEASURES} project={PROJECT} />)).toMatchSnapshot();
+ });
});
-it('should display tags', () => {
- const project = { ...PROJECT, tags: ['foo', 'bar'] };
- expect(shallow(<ProjectCard project={project} />)).toMatchSnapshot();
+describe('leak project card', () => {
+ it('should display analysis date and leak start date', () => {
+ const project = { ...PROJECT, leakPeriodDate: undefined, visibility: 'private' };
+ const card = shallow(<ProjectCard type="leak" measures={MEASURES} project={PROJECT} />);
+ const card2 = shallow(<ProjectCard type="leak" measures={MEASURES} project={project} />);
+ expect(card.find('.project-card-dates').exists()).toBeTruthy();
+ expect(card.find('.project-card-dates').find('span').getNodes()).toHaveLength(2);
+ expect(card.find('.project-card-dates').hasClass('width-100')).toBeFalsy();
+ expect(card2.find('.project-card-dates').find('span').getNodes()).toHaveLength(1);
+ expect(card2.find('.project-card-dates').hasClass('width-100')).toBeTruthy();
+ });
+
+ it('should not display analysis date or leak start date', () => {
+ const project = { ...PROJECT, analysisDate: undefined };
+ const card = shallow(<ProjectCard type="leak" measures={MEASURES} project={project} />);
+ expect(card.find('.project-card-dates').exists()).toBeFalsy();
+ });
+
+ it('should display loading', () => {
+ const measures = { ...MEASURES, new_bugs: undefined };
+ expect(
+ shallow(<ProjectCard type="leak" measures={measures} project={PROJECT} />)
+ .find('.boxed-group')
+ .hasClass('boxed-group-loading')
+ ).toBeTruthy();
+ });
+
+ it('should display tags', () => {
+ const project = { ...PROJECT, tags: ['foo', 'bar'] };
+ expect(
+ shallow(<ProjectCard type="leak" project={project} />).find('TagsList').exists()
+ ).toBeTruthy();
+ });
+
+ it('should private badge', () => {
+ const project = { ...PROJECT, visibility: 'private' };
+ expect(
+ shallow(<ProjectCard type="leak" project={project} />).find('PrivateBadge').exists()
+ ).toBeTruthy();
+ });
+
+ it('should display the leak measures and quality gate', () => {
+ expect(
+ shallow(<ProjectCard type="leak" measures={MEASURES} project={PROJECT} />)
+ ).toMatchSnapshot();
+ });
});
diff --git a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCard-test.js.snap b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCard-test.js.snap
index ff77d4e7b33..19805eb328a 100644
--- a/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCard-test.js.snap
+++ b/server/sonar-web/src/main/js/apps/projects/components/__tests__/__snapshots__/ProjectCard-test.js.snap
@@ -1,15 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
-exports[`should display loading 1`] = `
+exports[`leak project card should display the leak measures and quality gate 1`] = `
<div
- className="boxed-group project-card boxed-group-loading"
+ className="boxed-group project-card"
data-key="foo"
>
<div
- className="boxed-group-actions text-right"
- />
- <div
- className="boxed-group-header"
+ className="boxed-group-header clearfix"
>
<h2
className="project-card-name"
@@ -29,36 +26,49 @@ exports[`should display loading 1`] = `
Foo
</Link>
</h2>
+ <ProjectCardQualityGate
+ status="OK"
+ />
+ <div
+ className="pull-right text-right"
+ />
+ <div
+ className="project-card-dates note text-right pull-right"
+ >
+ <span>
+ projects.leak_period_x.a month ago
+ </span>
+ <span
+ className="big-spacer-left"
+ >
+ projects.last_analysis_on_x.March 1, 2017 9:36 AM
+ </span>
+ </div>
</div>
<div
className="boxed-group-inner"
>
- <ProjectCardMeasures />
+ <ProjectCardLeakMeasures
+ measures={
+ Object {
+ "alert_status": "OK",
+ "new_bugs": 12,
+ "reliability_rating": "1.0",
+ "sqale_rating": "1.0",
+ }
+ }
+ />
</div>
</div>
`;
-exports[`should display tags 1`] = `
+exports[`overall status project card should display the overall measures and quality gate 1`] = `
<div
- className="boxed-group project-card boxed-group-loading"
+ className="boxed-group project-card"
data-key="foo"
>
<div
- className="boxed-group-actions text-right"
- >
- <TagsList
- allowUpdate={false}
- customClass="spacer-left"
- tags={
- Array [
- "foo",
- "bar",
- ]
- }
- />
- </div>
- <div
- className="boxed-group-header"
+ className="boxed-group-header clearfix"
>
<h2
className="project-card-name"
@@ -78,13 +88,26 @@ exports[`should display tags 1`] = `
Foo
</Link>
</h2>
+ <ProjectCardQualityGate
+ status="OK"
+ />
+ <div
+ className="pull-right text-right"
+ />
</div>
<div
className="boxed-group-inner"
>
- <ProjectCardMeasures />
+ <ProjectCardOverallMeasures
+ measures={
+ Object {
+ "alert_status": "OK",
+ "new_bugs": 12,
+ "reliability_rating": "1.0",
+ "sqale_rating": "1.0",
+ }
+ }
+ />
</div>
</div>
`;
-
-exports[`should not display analysis date 1`] = `undefined`;
diff --git a/server/sonar-web/src/main/js/apps/projects/store/actions.js b/server/sonar-web/src/main/js/apps/projects/store/actions.js
index 7b5d11e2e47..38a2d21caca 100644
--- a/server/sonar-web/src/main/js/apps/projects/store/actions.js
+++ b/server/sonar-web/src/main/js/apps/projects/store/actions.js
@@ -194,7 +194,7 @@ export const fetchProjects = (query, isFavorite, organization) => dispatch => {
const data = convertToQueryData(query, isFavorite, organization, {
ps,
facets: FACETS.join(),
- f: 'analysisDate'
+ f: 'analysisDate,leakPeriodDate'
});
return searchProjects(data).then(onReceiveProjects(dispatch, query), onFail(dispatch));
};
@@ -206,7 +206,7 @@ export const fetchMoreProjects = (query, isFavorite, organization) => (dispatch,
const data = convertToQueryData(query, isFavorite, organization, {
ps: PAGE_SIZE,
p: pageIndex + 1,
- f: 'analysisDate'
+ f: 'analysisDate,leakPeriodDate'
});
return searchProjects(data).then(onReceiveMoreProjects(dispatch, query), onFail(dispatch));
};
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 a5c32750f3c..734c6bd7e4e 100644
--- a/server/sonar-web/src/main/js/apps/projects/styles.css
+++ b/server/sonar-web/src/main/js/apps/projects/styles.css
@@ -73,7 +73,7 @@
.project-card {
position: relative;
- min-height: 121px;
+ min-height: 114px;
box-sizing: border-box;
}
@@ -81,6 +81,11 @@
font-weight: 600;
}
+.project-card-dates {
+ margin-top: 4px;
+ margin-bottom: -16px;
+}
+
.project-card-measures {
margin: 0 -15px;
}