@@ -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 { |
@@ -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(); | |||
}); | |||
}); | |||
@@ -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> | |||
); |
@@ -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} |
@@ -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(); | |||
}); |
@@ -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(); | |||
}); |
@@ -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> | |||
`; |
@@ -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" |
@@ -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; | |||
}, []); |