--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`generateCoveredLinesMetric should correctly generate covered lines metric 1`] = `
+Object {
+ "data": Array [
+ Object {
+ "x": 2017-04-27T06:21:32.000Z,
+ "y": 88,
+ },
+ Object {
+ "x": 2017-04-30T21:06:24.000Z,
+ "y": 50,
+ },
+ ],
+ "name": "covered_lines",
+ "style": "style",
+ "translatedName": "project_activity.custom_metric.covered_lines",
+}
+`;
+
+exports[`generateSeries should correctly generate the series 1`] = `
+Array [
+ Object {
+ "data": Array [
+ Object {
+ "x": 2017-04-27T06:21:32.000Z,
+ "y": 100,
+ },
+ Object {
+ "x": 2017-04-30T21:06:24.000Z,
+ "y": 100,
+ },
+ ],
+ "name": "lines_to_cover",
+ "style": 1,
+ "translatedName": "metric.lines_to_cover.name",
+ },
+ Object {
+ "data": Array [
+ Object {
+ "x": 2017-04-27T06:21:32.000Z,
+ "y": 88,
+ },
+ Object {
+ "x": 2017-04-30T21:06:24.000Z,
+ "y": 50,
+ },
+ ],
+ "name": "covered_lines",
+ "style": 0,
+ "translatedName": "project_activity.custom_metric.covered_lines",
+ },
+]
+`;
+
+exports[`getAnalysesByVersionByDay should correctly map analysis by versions and by days 1`] = `
+Array [
+ Object {
+ "byDay": Object {
+ "2017-5-9": Array [
+ Object {
+ "date": 2017-06-09T11:06:10.000Z,
+ "events": Array [],
+ "key": "AVyMjlK1HjR_PLDzRbB9",
+ },
+ 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-16": Array [
+ Object {
+ "date": 2017-05-16T05:09:59.000Z,
+ "events": Array [
+ Object {
+ "category": "QUALITY_PROFILE",
+ "key": "AVwQF7zXl-nNFgFWOJ3W",
+ "name": "Changes in 'Default - SonarSource conventions' (Java)",
+ },
+ Object {
+ "category": "VERSION",
+ "key": "AVyM9oI1HjR_PLDzRciU",
+ "name": "1.0",
+ },
+ ],
+ "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",
+ },
+ Object {
+ "date": 2017-05-18T05:17:32.000Z,
+ "events": Array [],
+ "key": "AVwaa1qkpbBde8B6UhYI",
+ },
+ ],
+ "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 {
+ "2017-4-9": Array [
+ Object {
+ "date": 2017-05-09T10:03:59.000Z,
+ "events": Array [],
+ "key": "AVvtGF3IY6vCuQNDdwxI",
+ },
+ ],
+ },
+ "key": undefined,
+ "version": undefined,
+ },
+]
+`;
--- /dev/null
+/*
+ * 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.
+ */
+// @flow
+import * as utils from '../utils';
+
+const ANALYSES = [
+ { key: 'AVyMjlK1HjR_PLDzRbB9', date: new Date('2017-06-09T13:06:10+0200'), events: [] },
+ {
+ key: 'AVyM9n3cHjR_PLDzRciT',
+ date: new Date('2017-06-09T11:12:27+0200'),
+ events: [{ key: 'AVyM9oI1HjR_PLDzRciU', category: 'VERSION', name: '1.1-SNAPSHOT' }]
+ },
+ { key: 'AVyMjlK1HjR_PLDzRbB9', date: new Date('2017-06-09T11:12:27+0200'), events: [] },
+ {
+ key: 'AVxZtCpH7841nF4RNEMI',
+ date: new Date('2017-05-18T14:13:07+0200'),
+ events: [
+ {
+ key: 'AVxZtC-N7841nF4RNEMJ',
+ category: 'QUALITY_PROFILE',
+ name: 'Changes in \'Default - SonarSource conventions\' (Java)'
+ }
+ ]
+ },
+ { key: 'AVwaa1qkpbBde8B6UhYI', date: new Date('2017-05-18T07:17:32+0200'), events: [] },
+ {
+ key: 'AVwQF7kwl-nNFgFWOJ3V',
+ date: new Date('2017-05-16T07:09:59+0200'),
+ events: [
+ { key: 'AVyM9oI1HjR_PLDzRciU', category: 'VERSION', name: '1.0' },
+ {
+ key: 'AVwQF7zXl-nNFgFWOJ3W',
+ category: 'QUALITY_PROFILE',
+ name: 'Changes in \'Default - SonarSource conventions\' (Java)'
+ }
+ ]
+ },
+ { key: 'AVvtGF3IY6vCuQNDdwxI', date: new Date('2017-05-09T12:03:59+0200'), events: [] }
+];
+
+const HISTORY = [
+ {
+ metric: 'lines_to_cover',
+ history: [
+ { date: new Date('2017-04-27T08:21:32+0200'), value: '100' },
+ { date: new Date('2017-04-30T23:06:24+0200'), value: '100' }
+ ]
+ },
+ {
+ metric: 'uncovered_lines',
+ history: [
+ { date: new Date('2017-04-27T08:21:32+0200'), value: '12' },
+ { date: new Date('2017-04-30T23:06:24+0200'), value: '50' }
+ ]
+ }
+];
+
+const QUERY = {
+ category: '',
+ from: new Date('2017-04-27T08:21:32+0200'),
+ graph: 'overview',
+ project: 'foo',
+ to: undefined
+};
+
+jest.mock('moment', () => date => ({
+ startOf: () => {
+ return {
+ valueOf: () => `${date.getFullYear()}-${date.getMonth()}-${date.getDate()}`
+ };
+ },
+ toDate: () => new Date(date)
+}));
+
+describe('generateCoveredLinesMetric', () => {
+ it('should correctly generate covered lines metric', () => {
+ expect(utils.generateCoveredLinesMetric(HISTORY[1], HISTORY, 'style')).toMatchSnapshot();
+ });
+});
+
+describe('generateSeries', () => {
+ it('should correctly generate the series', () => {
+ expect(utils.generateSeries(HISTORY, 'coverage', 'INT')).toMatchSnapshot();
+ });
+});
+
+describe('getAnalysesByVersionByDay', () => {
+ it('should correctly map analysis by versions and by days', () => {
+ expect(utils.getAnalysesByVersionByDay(ANALYSES)).toMatchSnapshot();
+ });
+});
+
+describe('parseQuery', () => {
+ it('should parse query with default values', () => {
+ expect(
+ utils.parseQuery({
+ from: '2017-04-27T08:21:32+0200',
+ id: 'foo'
+ })
+ ).toEqual(QUERY);
+ });
+});
+
+describe('serializeQuery', () => {
+ it('should serialize query for api request', () => {
+ expect(utils.serializeQuery(QUERY)).toEqual({
+ from: '2017-04-27T06:21:32.000Z',
+ project: 'foo'
+ });
+ expect(utils.serializeQuery({ ...QUERY, graph: 'coverage', category: 'test' })).toEqual({
+ from: '2017-04-27T06:21:32.000Z',
+ project: 'foo',
+ category: 'test'
+ });
+ });
+});
+
+describe('serializeUrlQuery', () => {
+ it('should serialize query for url', () => {
+ expect(utils.serializeUrlQuery(QUERY)).toEqual({
+ from: '2017-04-27T06:21:32.000Z',
+ id: 'foo'
+ });
+ expect(utils.serializeUrlQuery({ ...QUERY, graph: 'coverage', category: 'test' })).toEqual({
+ from: '2017-04-27T06:21:32.000Z',
+ id: 'foo',
+ graph: 'coverage',
+ category: 'test'
+ });
+ });
+});
*/
// @flow
import React from 'react';
-import { debounce, sortBy } from 'lodash';
+import { debounce, findLast, maxBy, minBy, sortBy } from 'lodash';
import ProjectActivityGraphsHeader from './ProjectActivityGraphsHeader';
import GraphsZoom from './GraphsZoom';
import StaticGraphs from './StaticGraphs';
constructor(props: Props) {
super(props);
const series = generateSeries(props.measuresHistory, props.query.graph, props.metricsType);
- this.state = {
- graphStartDate: props.query.from || null,
- graphEndDate: props.query.to || null,
- series
- };
- this.updateQueryDateRange = debounce(this.updateQueryDateRange, 250);
+ this.state = { series, ...this.getStateZoomDates(null, props, series) };
+ this.updateQueryDateRange = debounce(this.updateQueryDateRange, 500);
}
componentWillReceiveProps(nextProps: Props) {
nextProps.measuresHistory !== this.props.measuresHistory ||
historyQueryChanged(this.props.query, nextProps.query)
) {
- this.setState({
- series: generateSeries(
- nextProps.measuresHistory,
- nextProps.query.graph,
- nextProps.metricsType
- )
- });
- }
- if (
- nextProps.query !== this.props.query &&
- datesQueryChanged(this.props.query, nextProps.query)
- ) {
- this.setState({ graphStartDate: nextProps.query.from, graphEndDate: nextProps.query.to });
+ const series = generateSeries(
+ nextProps.measuresHistory,
+ nextProps.query.graph,
+ nextProps.metricsType
+ );
+
+ const newDates = this.getStateZoomDates(this.props, nextProps, series);
+ if (newDates) {
+ this.setState({ series, ...newDates });
+ } else {
+ this.setState({ series });
+ }
}
}
+ getStateZoomDates = (props: ?Props, nextProps: Props, series: Array<Serie>) => {
+ const newDates = { from: nextProps.query.from || null, to: nextProps.query.to || null };
+ if (props && datesQueryChanged(props.query, nextProps.query)) {
+ return { graphEndDate: newDates.to, graphStartDate: newDates.from };
+ }
+ if (newDates.to == null && newDates.from == null) {
+ const firstValid = minBy(series.map(serie => serie.data.find(p => p.y || p.y === 0)), 'x');
+ const lastValid = maxBy(
+ series.map(serie => findLast(serie.data, p => p.y || p.y === 0)),
+ 'x'
+ );
+ return {
+ graphEndDate: lastValid ? lastValid.x : newDates.to,
+ graphStartDate: firstValid ? firstValid.x : newDates.from
+ };
+ }
+ if (!props) {
+ return { graphEndDate: newDates.to, graphStartDate: newDates.from };
+ }
+ };
+
updateGraphZoom = (graphStartDate: ?Date, graphEndDate: ?Date) => {
if (graphEndDate != null && graphStartDate != null) {
const msDiff = Math.abs(graphEndDate.valueOf() - graphStartDate.valueOf());
exports[`should render correctly with filter history on dates 1`] = `
Object {
+ "graphEndDate": null,
+ "graphStartDate": "2016-10-27T12:21:15+0200",
"series": Array [
Object {
"data": Array [