diff options
Diffstat (limited to 'server')
9 files changed, 698 insertions, 185 deletions
diff --git a/server/sonar-web/src/main/js/apps/projectActivity/__tests__/__snapshots__/utils-test.js.snap b/server/sonar-web/src/main/js/apps/projectActivity/__tests__/__snapshots__/utils-test.js.snap index e9144bf2e16..14647a21294 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/__tests__/__snapshots__/utils-test.js.snap +++ b/server/sonar-web/src/main/js/apps/projectActivity/__tests__/__snapshots__/utils-test.js.snap @@ -53,6 +53,108 @@ Array [ ] `; +exports[`getAnalysesByVersionByDay should also filter analysis based on the query 1`] = ` +Array [ + Object { + "byDay": Object { + "2017-4-16": Array [ + Object { + "date": 2017-05-16T05:09:59.000Z, + "events": Array [ + Object { + "category": "VERSION", + "key": "AVyM9oI1HjR_PLDzRciU", + "name": "1.0", + }, + Object { + "category": "QUALITY_PROFILE", + "key": "AVwQF7zXl-nNFgFWOJ3W", + "name": "Changes in 'Default - SonarSource conventions' (Java)", + }, + ], + "key": "AVwQF7kwl-nNFgFWOJ3V", + }, + ], + "2017-4-18": Array [ + Object { + "date": 2017-05-18T12:13:07.000Z, + "events": Array [ + Object { + "category": "QUALITY_PROFILE", + "key": "AVxZtC-N7841nF4RNEMJ", + "name": "Changes in 'Default - SonarSource conventions' (Java)", + }, + ], + "key": "AVxZtCpH7841nF4RNEMI", + }, + ], + }, + "key": "AVyM9oI1HjR_PLDzRciU", + "version": "1.0", + }, + Object { + "byDay": Object {}, + "key": undefined, + "version": undefined, + }, +] +`; + +exports[`getAnalysesByVersionByDay should also filter analysis based on the query 2`] = ` +Array [ + Object { + "byDay": Object { + "2017-5-9": Array [ + Object { + "date": 2017-06-09T09:12:27.000Z, + "events": Array [ + Object { + "category": "VERSION", + "key": "AVyM9oI1HjR_PLDzRciU", + "name": "1.1-SNAPSHOT", + }, + ], + "key": "AVyM9n3cHjR_PLDzRciT", + }, + ], + }, + "key": "AVyM9oI1HjR_PLDzRciU", + "version": "1.1-SNAPSHOT", + }, + Object { + "byDay": Object { + "2017-4-18": Array [ + Object { + "date": 2017-05-18T12:13:07.000Z, + "events": Array [ + Object { + "category": "QUALITY_PROFILE", + "key": "AVxZtC-N7841nF4RNEMJ", + "name": "Changes in 'Default - SonarSource conventions' (Java)", + }, + ], + "key": "AVxZtCpH7841nF4RNEMI", + }, + ], + "2017-5-9": Array [ + Object { + "date": 2017-06-09T09:12:27.000Z, + "events": Array [], + "key": "AVyMjlK1HjR_PLDzRbB9", + }, + ], + }, + "key": "AVyM9oI1HjR_PLDzRciU", + "version": "1.0", + }, + Object { + "byDay": Object {}, + "key": undefined, + "version": undefined, + }, +] +`; + exports[`getAnalysesByVersionByDay should correctly map analysis by versions and by days 1`] = ` Array [ Object { diff --git a/server/sonar-web/src/main/js/apps/projectActivity/__tests__/utils-test.js b/server/sonar-web/src/main/js/apps/projectActivity/__tests__/utils-test.js index c165f9740b8..b3e4f2b28cf 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/__tests__/utils-test.js +++ b/server/sonar-web/src/main/js/apps/projectActivity/__tests__/utils-test.js @@ -108,7 +108,34 @@ describe('generateSeries', () => { describe('getAnalysesByVersionByDay', () => { it('should correctly map analysis by versions and by days', () => { - expect(utils.getAnalysesByVersionByDay(ANALYSES)).toMatchSnapshot(); + expect( + utils.getAnalysesByVersionByDay(ANALYSES, { + category: '', + customMetrics: [], + graph: 'overview', + project: 'foo' + }) + ).toMatchSnapshot(); + }); + it('should also filter analysis based on the query', () => { + expect( + utils.getAnalysesByVersionByDay(ANALYSES, { + category: 'QUALITY_PROFILE', + customMetrics: [], + graph: 'overview', + project: 'foo' + }) + ).toMatchSnapshot(); + expect( + utils.getAnalysesByVersionByDay(ANALYSES, { + category: '', + customMetrics: [], + graph: 'overview', + project: 'foo', + to: new Date('2017-06-09T11:12:27+0200'), + from: new Date('2017-05-18T14:13:07+0200') + }) + ).toMatchSnapshot(); }); }); diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.js index a0a8a41dd31..2706f9d2b14 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.js +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityAnalysesList.js @@ -65,30 +65,48 @@ export default class ProjectActivityAnalysesList extends React.PureComponent { } componentDidUpdate(prevProps: Props) { - if (this.scrollContainer) { - const selectedDateChanged = selectedDateQueryChanged(prevProps.query, this.props.query); - if (selectedDateChanged || prevProps.analysis !== this.props.analyses) { - if (selectedDateChanged && this.props.query.selectedDate) { - const selectedDate = this.props.query.selectedDate.valueOf(); - for (let i = 1; i < this.analyses.length; i++) { - if (Number(this.analyses[i].getAttribute('data-date')) === selectedDate) { - const containerHeight = this.scrollContainer.offsetHeight - 100; - const scrollDiff = Math.abs( - this.scrollContainer.scrollTop - this.analyses[i].offsetTop - ); - // Center only the extremities and the ones outside of the container - if (scrollDiff > containerHeight || scrollDiff < 100) { - this.resetScrollTop(this.analyses[i].offsetTop - containerHeight / 2); - } - break; - } - } - } else if (activityQueryChanged(prevProps.query, this.props.query)) { - this.resetScrollTop(0, true); + if (!this.scrollContainer) { + return; + } + if ( + this.props.query.selectedDate && + (selectedDateQueryChanged(prevProps.query, this.props.query) || + prevProps.analyses !== this.props.analyses) + ) { + this.scrollToDate(this.props.query.selectedDate); + } else if (activityQueryChanged(prevProps.query, this.props.query)) { + this.resetScrollTop(0, true); + } + } + + handleScroll = () => this.updateStickyBadges(true); + + resetScrollTop = (newScrollTop: number, forceBadgeAlignement?: boolean) => { + this.scrollContainer.scrollTop = newScrollTop; + for (let i = 1; i < this.badges.length; i++) { + this.badges[i].removeAttribute('originOffsetTop'); + this.badges[i].classList.remove('sticky'); + } + this.updateStickyBadges(forceBadgeAlignement); + }; + + scrollToDate = (targetDate: ?Date) => { + if (!this.scrollContainer || !targetDate) { + return; + } + const date = targetDate.valueOf(); + for (let i = 1; i < this.analyses.length; i++) { + if (Number(this.analyses[i].getAttribute('data-date')) === date) { + const containerHeight = this.scrollContainer.offsetHeight - 100; + const scrollDiff = Math.abs(this.scrollContainer.scrollTop - this.analyses[i].offsetTop); + // Center only the extremities and the ones outside of the container + if (scrollDiff > containerHeight || scrollDiff < 100) { + this.resetScrollTop(this.analyses[i].offsetTop - containerHeight / 2); } + break; } } - } + }; updateStickyBadges = (forceBadgeAlignement?: boolean) => { if (this.scrollContainer && this.badges) { @@ -119,23 +137,14 @@ export default class ProjectActivityAnalysesList extends React.PureComponent { } }; - handleScroll = () => this.updateStickyBadges(true); - - resetScrollTop = (newScrollTop: number, forceBadgeAlignement?: boolean) => { - this.scrollContainer.scrollTop = newScrollTop; - for (let i = 1; i < this.badges.length; i++) { - this.badges[i].removeAttribute('originOffsetTop'); - this.badges[i].classList.remove('sticky'); - } - this.updateStickyBadges(forceBadgeAlignement); - }; - - updateSelectedDate = (date: Date) => { - this.props.updateQuery({ selectedDate: date }); - }; + updateSelectedDate = (date: Date) => this.props.updateQuery({ selectedDate: date }); render() { - if (this.props.analyses.length === 0) { + const byVersionByDay = getAnalysesByVersionByDay(this.props.analyses, this.props.query); + const hasFilteredData = + byVersionByDay.length > 1 || + (byVersionByDay.length === 1 && Object.keys(byVersionByDay[0].byDay).length > 0); + if (this.props.analyses.length === 0 || !hasFilteredData) { return ( <div className={this.props.className}> {this.props.loading @@ -146,7 +155,6 @@ export default class ProjectActivityAnalysesList extends React.PureComponent { } const firstAnalysisKey = this.props.analyses[0].key; - const byVersionByDay = getAnalysesByVersionByDay(this.props.analyses); const selectedDate = this.props.query.selectedDate ? this.props.query.selectedDate.valueOf() : null; @@ -155,46 +163,52 @@ export default class ProjectActivityAnalysesList extends React.PureComponent { className={classNames('project-activity-versions-list', this.props.className)} onScroll={this.handleScroll} ref={element => (this.scrollContainer = element)}> - {byVersionByDay.map((version, idx) => ( - <li key={version.key || 'noversion'}> - {version.version && - <div className={classNames('project-activity-version-badge', { first: idx === 0 })}> - <span className="badge"> - {version.version} - </span> - </div>} - <ul className="project-activity-days-list"> - {Object.keys(version.byDay).map(day => ( - <li - key={day} - className="project-activity-day" - data-day={moment(Number(day)).format('YYYY-MM-DD')}> - <div className="project-activity-date"> - <FormattedDate date={Number(day)} format="LL" /> - </div> - <ul className="project-activity-analyses-list"> - {version.byDay[day] != null && - version.byDay[day].map(analysis => ( - <ProjectActivityAnalysis - addCustomEvent={this.props.addCustomEvent} - addVersion={this.props.addVersion} - analysis={analysis} - canAdmin={this.props.canAdmin} - changeEvent={this.props.changeEvent} - deleteAnalysis={this.props.deleteAnalysis} - deleteEvent={this.props.deleteEvent} - isFirst={analysis.key === firstAnalysisKey} - key={analysis.key} - selected={analysis.date.valueOf() === selectedDate} - updateSelectedDate={this.updateSelectedDate} - /> - ))} - </ul> - </li> - ))} - </ul> - </li> - ))} + {byVersionByDay.map((version, idx) => { + const days = Object.keys(version.byDay); + if (days.length <= 0) { + return null; + } + return ( + <li key={version.key || 'noversion'}> + {version.version && + <div className={classNames('project-activity-version-badge', { first: idx === 0 })}> + <span className="badge"> + {version.version} + </span> + </div>} + <ul className="project-activity-days-list"> + {days.map(day => ( + <li + key={day} + className="project-activity-day" + data-day={moment(Number(day)).format('YYYY-MM-DD')}> + <div className="project-activity-date"> + <FormattedDate date={Number(day)} format="LL" /> + </div> + <ul className="project-activity-analyses-list"> + {version.byDay[day] != null && + version.byDay[day].map(analysis => ( + <ProjectActivityAnalysis + addCustomEvent={this.props.addCustomEvent} + addVersion={this.props.addVersion} + analysis={analysis} + canAdmin={this.props.canAdmin} + changeEvent={this.props.changeEvent} + deleteAnalysis={this.props.deleteAnalysis} + deleteEvent={this.props.deleteEvent} + isFirst={analysis.key === firstAnalysisKey} + key={analysis.key} + selected={analysis.date.valueOf() === selectedDate} + updateSelectedDate={this.updateSelectedDate} + /> + ))} + </ul> + </li> + ))} + </ul> + </li> + ); + })} {this.props.analysesLoading && <li className="text-center"><i className="spinner" /></li>} </ul> ); diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js index 59c9485d937..e3a3b582796 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/ProjectActivityApp.js @@ -24,7 +24,7 @@ import moment from 'moment'; import ProjectActivityPageHeader from './ProjectActivityPageHeader'; import ProjectActivityAnalysesList from './ProjectActivityAnalysesList'; import ProjectActivityGraphs from './ProjectActivityGraphs'; -import { getDisplayedHistoryMetrics, activityQueryChanged } from '../utils'; +import { getDisplayedHistoryMetrics } from '../utils'; import { translate } from '../../../helpers/l10n'; import './projectActivity.css'; import type { Analysis, MeasureHistory, Metric, Query } from '../types'; @@ -46,40 +46,8 @@ type Props = { updateQuery: (newQuery: Query) => void }; -type State = { - filteredAnalyses: Array<Analysis> -}; - export default class ProjectActivityApp extends React.PureComponent { props: Props; - state: State; - - constructor(props: Props) { - super(props); - this.state = { filteredAnalyses: this.filterAnalyses(props.analyses, props.query) }; - } - - componentWillReceiveProps(nextProps: Props) { - if ( - nextProps.analyses !== this.props.analyses || - activityQueryChanged(this.props.query, nextProps.query) - ) { - this.setState({ filteredAnalyses: this.filterAnalyses(nextProps.analyses, nextProps.query) }); - } - } - - filterAnalyses = (analyses: Array<Analysis>, query: Query): Array<Analysis> => { - if (!query.category && !query.from && !query.to) { - return analyses; - } - return analyses.filter(analysis => { - const isAfterFrom = !query.from || analysis.date >= query.from; - const isBeforeTo = !query.to || analysis.date <= query.to; - const hasSelectedCategoryEvents = - !query.category || analysis.events.find(event => event.category === query.category) != null; - return isAfterFrom && isBeforeTo && hasSelectedCategoryEvents; - }); - }; getMetricType = () => { const historyMetrics = getDisplayedHistoryMetrics( @@ -92,8 +60,7 @@ export default class ProjectActivityApp extends React.PureComponent { }; render() { - const { measuresHistory, query } = this.props; - const { filteredAnalyses } = this.state; + const { analyses, measuresHistory, query } = this.props; const { configuration } = this.props.project; const canAdmin = configuration ? configuration.showHistory : false; return ( @@ -113,7 +80,7 @@ export default class ProjectActivityApp extends React.PureComponent { addCustomEvent={this.props.addCustomEvent} addVersion={this.props.addVersion} analysesLoading={this.props.analysesLoading} - analyses={filteredAnalyses} + analyses={analyses} canAdmin={canAdmin} className="boxed-group-inner" changeEvent={this.props.changeEvent} @@ -126,7 +93,7 @@ export default class ProjectActivityApp extends React.PureComponent { </div> <div className="project-activity-layout-page-main"> <ProjectActivityGraphs - analyses={filteredAnalyses} + analyses={analyses} leakPeriodDate={moment(this.props.project.leakPeriodDate).toDate()} loading={this.props.graphLoading} measuresHistory={measuresHistory} diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityAnalysesList-test.js b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityAnalysesList-test.js new file mode 100644 index 00000000000..762c2d5efe3 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityAnalysesList-test.js @@ -0,0 +1,116 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 React from 'react'; +import { shallow } from 'enzyme'; +import ProjectActivityAnalysesList from '../ProjectActivityAnalysesList'; + +const ANALYSES = [ + { + key: 'A1', + date: new Date('2016-10-27T16:33:50+0000'), + events: [ + { + key: 'E1', + category: 'VERSION', + name: '6.5-SNAPSHOT' + } + ] + }, + { + key: 'A2', + date: new Date('2016-10-27T12:21:15+0000'), + events: [] + }, + { + key: 'A3', + date: new Date('2016-10-26T12:17:29+0000'), + events: [ + { + key: 'E2', + category: 'VERSION', + name: '6.4' + }, + { + key: 'E3', + category: 'OTHER', + name: 'foo' + } + ] + }, + { + key: 'A4', + date: new Date('2016-10-24T16:33:50+0000'), + events: [ + { + key: 'E1', + category: 'QUALITY_GATE', + name: 'Quality gate changed to red...' + } + ] + } +]; + +const DEFAULT_PROPS = { + addCustomEvent: () => {}, + addVersion: () => {}, + analyses: ANALYSES, + analysesLoading: false, + canAdmin: false, + changeEvent: () => {}, + deleteAnalysis: () => {}, + deleteEvent: () => {}, + loading: false, + query: { category: '', graph: 'overview', project: 'org.sonarsource.sonarqube:sonarqube' }, + updateQuery: () => {} +}; + +jest.mock('moment', () => date => ({ + startOf: () => { + return { + valueOf: () => `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}` + }; + }, + toDate: () => new Date(date), + format: format => `Formated.${format}:${date}` +})); + +window.Number = val => val; + +it('should render correctly', () => { + expect(shallow(<ProjectActivityAnalysesList {...DEFAULT_PROPS} />)).toMatchSnapshot(); +}); + +it('should correctly filter analyses by category', () => { + const wrapper = shallow(<ProjectActivityAnalysesList {...DEFAULT_PROPS} />); + wrapper.setProps({ query: { ...DEFAULT_PROPS.query, category: 'QUALITY_GATE' } }); + expect(wrapper).toMatchSnapshot(); +}); + +it('should correctly filter analyses by date range', () => { + const wrapper = shallow(<ProjectActivityAnalysesList {...DEFAULT_PROPS} />); + wrapper.setProps({ + query: { + ...DEFAULT_PROPS.query, + from: new Date('2016-10-27T16:33:50+0000'), + to: new Date('2016-10-27T16:33:50+0000') + } + }); + expect(wrapper).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityApp-test.js b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityApp-test.js index 22f85b2a28f..21c0a40d10c 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityApp-test.js +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/ProjectActivityApp-test.js @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import React from 'react'; -import { mount, shallow } from 'enzyme'; +import { shallow } from 'enzyme'; import ProjectActivityApp from '../ProjectActivityApp'; const ANALYSES = [ @@ -87,21 +87,3 @@ const DEFAULT_PROPS = { it('should render correctly', () => { expect(shallow(<ProjectActivityApp {...DEFAULT_PROPS} />)).toMatchSnapshot(); }); - -it('should correctly filter analyses by category', () => { - const wrapper = mount(<ProjectActivityApp {...DEFAULT_PROPS} />); - wrapper.setProps({ query: { ...DEFAULT_PROPS.query, category: 'VERSION' } }); - expect(wrapper.state()).toMatchSnapshot(); -}); - -it('should correctly filter analyses by date range', () => { - const wrapper = mount(<ProjectActivityApp {...DEFAULT_PROPS} />); - wrapper.setProps({ - query: { - ...DEFAULT_PROPS.query, - from: new Date('2016-10-27T12:21:15+0200'), - to: new Date('2016-10-27T12:21:15+0200') - } - }); - expect(wrapper.state()).toMatchSnapshot(); -}); diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityAnalysesList-test.js.snap b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityAnalysesList-test.js.snap new file mode 100644 index 00000000000..53dc51ec351 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityAnalysesList-test.js.snap @@ -0,0 +1,336 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should correctly filter analyses by category 1`] = ` +<ul + className="project-activity-versions-list" + onScroll={[Function]} +> + <li> + <div + className="project-activity-version-badge first" + > + <span + className="badge" + > + 6.4 + </span> + </div> + <ul + className="project-activity-days-list" + > + <li + className="project-activity-day" + data-day="Formated.YYYY-MM-DD:2016-9-24" + > + <div + className="project-activity-date" + > + <FormattedDate + date="2016-9-24" + format="LL" + /> + </div> + <ul + className="project-activity-analyses-list" + > + <ProjectActivityAnalysis + addCustomEvent={[Function]} + addVersion={[Function]} + analysis={ + Object { + "date": 2016-10-24T16:33:50.000Z, + "events": Array [ + Object { + "category": "QUALITY_GATE", + "key": "E1", + "name": "Quality gate changed to red...", + }, + ], + "key": "A4", + } + } + canAdmin={false} + changeEvent={[Function]} + deleteAnalysis={[Function]} + deleteEvent={[Function]} + isFirst={false} + selected={false} + updateSelectedDate={[Function]} + /> + </ul> + </li> + </ul> + </li> +</ul> +`; + +exports[`should correctly filter analyses by date range 1`] = ` +<ul + className="project-activity-versions-list" + onScroll={[Function]} +> + <li> + <div + className="project-activity-version-badge first" + > + <span + className="badge" + > + 6.5-SNAPSHOT + </span> + </div> + <ul + className="project-activity-days-list" + > + <li + className="project-activity-day" + data-day="Formated.YYYY-MM-DD:2016-9-27" + > + <div + className="project-activity-date" + > + <FormattedDate + date="2016-9-27" + format="LL" + /> + </div> + <ul + className="project-activity-analyses-list" + > + <ProjectActivityAnalysis + addCustomEvent={[Function]} + addVersion={[Function]} + analysis={ + Object { + "date": 2016-10-27T16:33:50.000Z, + "events": Array [ + Object { + "category": "VERSION", + "key": "E1", + "name": "6.5-SNAPSHOT", + }, + ], + "key": "A1", + } + } + canAdmin={false} + changeEvent={[Function]} + deleteAnalysis={[Function]} + deleteEvent={[Function]} + isFirst={true} + selected={false} + updateSelectedDate={[Function]} + /> + </ul> + </li> + </ul> + </li> +</ul> +`; + +exports[`should render correctly 1`] = ` +<ul + className="project-activity-versions-list" + onScroll={[Function]} +> + <li> + <div + className="project-activity-version-badge first" + > + <span + className="badge" + > + 6.5-SNAPSHOT + </span> + </div> + <ul + className="project-activity-days-list" + > + <li + className="project-activity-day" + data-day="Formated.YYYY-MM-DD:2016-9-27" + > + <div + className="project-activity-date" + > + <FormattedDate + date="2016-9-27" + format="LL" + /> + </div> + <ul + className="project-activity-analyses-list" + > + <ProjectActivityAnalysis + addCustomEvent={[Function]} + addVersion={[Function]} + analysis={ + Object { + "date": 2016-10-27T16:33:50.000Z, + "events": Array [ + Object { + "category": "VERSION", + "key": "E1", + "name": "6.5-SNAPSHOT", + }, + ], + "key": "A1", + } + } + canAdmin={false} + changeEvent={[Function]} + deleteAnalysis={[Function]} + deleteEvent={[Function]} + isFirst={true} + selected={false} + updateSelectedDate={[Function]} + /> + </ul> + </li> + </ul> + </li> + <li> + <div + className="project-activity-version-badge" + > + <span + className="badge" + > + 6.4 + </span> + </div> + <ul + className="project-activity-days-list" + > + <li + className="project-activity-day" + data-day="Formated.YYYY-MM-DD:2016-9-27" + > + <div + className="project-activity-date" + > + <FormattedDate + date="2016-9-27" + format="LL" + /> + </div> + <ul + className="project-activity-analyses-list" + > + <ProjectActivityAnalysis + addCustomEvent={[Function]} + addVersion={[Function]} + analysis={ + Object { + "date": 2016-10-27T12:21:15.000Z, + "events": Array [], + "key": "A2", + } + } + canAdmin={false} + changeEvent={[Function]} + deleteAnalysis={[Function]} + deleteEvent={[Function]} + isFirst={false} + selected={false} + updateSelectedDate={[Function]} + /> + </ul> + </li> + <li + className="project-activity-day" + data-day="Formated.YYYY-MM-DD:2016-9-26" + > + <div + className="project-activity-date" + > + <FormattedDate + date="2016-9-26" + format="LL" + /> + </div> + <ul + className="project-activity-analyses-list" + > + <ProjectActivityAnalysis + addCustomEvent={[Function]} + addVersion={[Function]} + analysis={ + Object { + "date": 2016-10-26T12:17:29.000Z, + "events": Array [ + Object { + "category": "VERSION", + "key": "E2", + "name": "6.4", + }, + Object { + "category": "OTHER", + "key": "E3", + "name": "foo", + }, + ], + "key": "A3", + } + } + canAdmin={false} + changeEvent={[Function]} + deleteAnalysis={[Function]} + deleteEvent={[Function]} + isFirst={false} + selected={false} + updateSelectedDate={[Function]} + /> + </ul> + </li> + </ul> + </li> + <li> + <ul + className="project-activity-days-list" + > + <li + className="project-activity-day" + data-day="Formated.YYYY-MM-DD:2016-9-24" + > + <div + className="project-activity-date" + > + <FormattedDate + date="2016-9-24" + format="LL" + /> + </div> + <ul + className="project-activity-analyses-list" + > + <ProjectActivityAnalysis + addCustomEvent={[Function]} + addVersion={[Function]} + analysis={ + Object { + "date": 2016-10-24T16:33:50.000Z, + "events": Array [ + Object { + "category": "QUALITY_GATE", + "key": "E1", + "name": "Quality gate changed to red...", + }, + ], + "key": "A4", + } + } + canAdmin={false} + changeEvent={[Function]} + deleteAnalysis={[Function]} + deleteEvent={[Function]} + isFirst={false} + selected={false} + updateSelectedDate={[Function]} + /> + </ul> + </li> + </ul> + </li> +</ul> +`; diff --git a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityApp-test.js.snap b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityApp-test.js.snap index 7fa450f12c8..f966955c248 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityApp-test.js.snap +++ b/server/sonar-web/src/main/js/apps/projectActivity/components/__tests__/__snapshots__/ProjectActivityApp-test.js.snap @@ -1,51 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should correctly filter analyses by category 1`] = ` -Object { - "filteredAnalyses": Array [ - Object { - "date": 2016-10-27T14:33:50.000Z, - "events": Array [ - Object { - "category": "VERSION", - "key": "E1", - "name": "6.5-SNAPSHOT", - }, - ], - "key": "A1", - }, - Object { - "date": 2016-10-26T10:17:29.000Z, - "events": Array [ - Object { - "category": "VERSION", - "key": "E2", - "name": "6.4", - }, - Object { - "category": "OTHER", - "key": "E3", - "name": "foo", - }, - ], - "key": "A3", - }, - ], -} -`; - -exports[`should correctly filter analyses by date range 1`] = ` -Object { - "filteredAnalyses": Array [ - Object { - "date": 2016-10-27T10:21:15.000Z, - "events": Array [], - "key": "A2", - }, - ], -} -`; - exports[`should render correctly 1`] = ` <div className="page page-limited" diff --git a/server/sonar-web/src/main/js/apps/projectActivity/utils.js b/server/sonar-web/src/main/js/apps/projectActivity/utils.js index a42c389972c..6ea4c782138 100644 --- a/server/sonar-web/src/main/js/apps/projectActivity/utils.js +++ b/server/sonar-web/src/main/js/apps/projectActivity/utils.js @@ -130,7 +130,8 @@ export const generateSeries = ( }; export const getAnalysesByVersionByDay = ( - analyses: Array<Analysis> + analyses: Array<Analysis>, + query: Query ): Array<{ version: ?string, key: ?string, @@ -142,16 +143,30 @@ export const getAnalysesByVersionByDay = ( } const currentVersion = acc[acc.length - 1]; const day = moment(analysis.date).startOf('day').valueOf().toString(); - if (!currentVersion.byDay[day]) { - currentVersion.byDay[day] = []; + + let matchFilters = true; + if (query.category || query.from || query.to) { + const isAfterFrom = !query.from || analysis.date >= query.from; + const isBeforeTo = !query.to || analysis.date <= query.to; + const hasSelectedCategoryEvents = + !query.category || analysis.events.find(event => event.category === query.category) != null; + matchFilters = isAfterFrom && isBeforeTo && hasSelectedCategoryEvents; + } + + if (matchFilters) { + if (!currentVersion.byDay[day]) { + currentVersion.byDay[day] = []; + } + currentVersion.byDay[day].push(analysis); } - currentVersion.byDay[day].push(analysis); const versionEvent = analysis.events.find(event => event.category === 'VERSION'); if (versionEvent && versionEvent.category === 'VERSION') { currentVersion.version = versionEvent.name; currentVersion.key = versionEvent.key; - acc.push({ version: undefined, key: undefined, byDay: {} }); + if (Object.keys(currentVersion.byDay).length > 0) { + acc.push({ version: undefined, key: undefined, byDay: {} }); + } } return acc; }, []); |