diff options
author | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-05-24 15:06:17 +0200 |
---|---|---|
committer | Grégoire Aubert <gregoire.aubert@sonarsource.com> | 2017-06-09 08:26:48 +0200 |
commit | 2fb2871f4be5ea216ab71e67b80622c040fd1791 (patch) | |
tree | c4700ff5f5dbda793a6f7577925ba345abf37f19 /server/sonar-web | |
parent | 5ad1c512b2f8f3a07cfd72ca803c66d4c6c31988 (diff) | |
download | sonarqube-2fb2871f4be5ea216ab71e67b80622c040fd1791.tar.gz sonarqube-2fb2871f4be5ea216ab71e67b80622c040fd1791.zip |
SONAR-9245 Add the leak period start date
Diffstat (limited to 'server/sonar-web')
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; } |