]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7402 display complementary measures
authorStas Vilchik <vilchiks@gmail.com>
Wed, 6 Apr 2016 06:39:45 +0000 (08:39 +0200)
committerStas Vilchik <vilchiks@gmail.com>
Wed, 6 Apr 2016 09:58:14 +0000 (11:58 +0200)
13 files changed:
it/it-tests/src/test/resources/measure/ProjectMeasuresPageTest/should_drilldown_on_list_view.html
it/it-tests/src/test/resources/measure/ProjectMeasuresPageTest/should_drilldown_on_tree_view.html
server/sonar-web/src/main/js/apps/component-measures/config/complementary.js [new file with mode: 0644]
server/sonar-web/src/main/js/apps/component-measures/details/drilldown/ComponentsList.js
server/sonar-web/src/main/js/apps/component-measures/details/drilldown/ComponentsListRow.js
server/sonar-web/src/main/js/apps/component-measures/details/drilldown/ListView.js
server/sonar-web/src/main/js/apps/component-measures/details/drilldown/ListViewContainer.js
server/sonar-web/src/main/js/apps/component-measures/details/drilldown/MeasureCell.js
server/sonar-web/src/main/js/apps/component-measures/details/drilldown/TreeView.js
server/sonar-web/src/main/js/apps/component-measures/details/drilldown/TreeViewContainer.js
server/sonar-web/src/main/js/apps/component-measures/store/listViewActions.js
server/sonar-web/src/main/js/apps/component-measures/store/treeViewActions.js
server/sonar-web/src/main/js/apps/component-measures/utils.js

index d7d6085f6718c6b5f8a0dff804e4ed80c0eac5ba..e68d8b87dfb0a1fd4892133b16892a43fa798b44 100644 (file)
@@ -25,7 +25,7 @@
 </tr>
 <tr>
        <td>assertText</td>
-       <td>id=component-measures-component-measure-project-measures-page-test-project:src/main/xoo/sample/Sample.xoo</td>
+       <td>id=component-measures-component-measure-project-measures-page-test-project:src/main/xoo/sample/Sample.xoo-ncloc</td>
        <td>*13*</td>
 </tr>
 <tr>
index a654f8e5c0efa77704f673973a312e43f6bd1a8a..489c9d93afc8409b9f5cbcd0b1a59f911bffcb42 100644 (file)
@@ -25,7 +25,7 @@
 </tr>
 <tr>
        <td>assertText</td>
-       <td>id=component-measures-component-measure-project-measures-page-test-project:src/main/xoo/sample</td>
+       <td>id=component-measures-component-measure-project-measures-page-test-project:src/main/xoo/sample-ncloc</td>
        <td>*13*</td>
 </tr>
 <tr>
@@ -45,7 +45,7 @@
 </tr>
 <tr>
        <td>assertText</td>
-       <td>id=component-measures-component-measure-project-measures-page-test-project:src/main/xoo/sample/Sample.xoo</td>
+       <td>id=component-measures-component-measure-project-measures-page-test-project:src/main/xoo/sample/Sample.xoo-ncloc</td>
        <td>*13*</td>
 </tr>
 <tr>
diff --git a/server/sonar-web/src/main/js/apps/component-measures/config/complementary.js b/server/sonar-web/src/main/js/apps/component-measures/config/complementary.js
new file mode 100644 (file)
index 0000000..8727880
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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.
+ */
+export default {
+  'overall_coverage': ['overall_uncovered_lines', 'overall_uncovered_conditions'],
+  'overall_line_coverage': ['overall_uncovered_lines'],
+  'overall_branch_coverage': ['overall_uncovered_conditions'],
+  'overall_uncovered_lines': ['overall_line_coverage'],
+  'overall_uncovered_conditions': ['overall_branch_coverage'],
+
+  'new_overall_coverage': ['new_overall_uncovered_lines', 'new_overall_uncovered_conditions'],
+  'new_overall_line_coverage': ['new_overall_uncovered_lines'],
+  'new_overall_branch_coverage': ['new_overall_uncovered_conditions'],
+  'new_overall_uncovered_lines': ['new_overall_line_coverage'],
+  'new_overall_uncovered_conditions': ['new_overall_branch_coverage'],
+
+  'coverage': ['uncovered_lines', 'uncovered_conditions'],
+  'line_coverage': ['uncovered_lines'],
+  'branch_coverage': ['uncovered_conditions'],
+  'uncovered_lines': ['line_coverage'],
+  'uncovered_conditions': ['branch_coverage'],
+
+  'new_coverage': ['new_uncovered_lines', 'new_uncovered_conditions'],
+  'new_line_coverage': ['new_uncovered_lines'],
+  'new_branch_coverage': ['new_uncovered_conditions'],
+  'new_uncovered_lines': ['new_line_coverage'],
+  'new_uncovered_conditions': ['new_branch_coverage'],
+
+  'it_coverage': ['it_uncovered_lines', 'it_uncovered_conditions'],
+  'it_line_coverage': ['it_uncovered_lines'],
+  'it_branch_coverage': ['it_uncovered_conditions'],
+  'it_uncovered_lines': ['it_line_coverage'],
+  'it_uncovered_conditions': ['it_branch_coverage'],
+
+  'new_it_coverage': ['new_it_uncovered_lines', 'new_it_uncovered_conditions'],
+  'new_it_line_coverage': ['new_it_uncovered_lines'],
+  'new_it_branch_coverage': ['new_it_uncovered_conditions'],
+  'new_it_uncovered_lines': ['new_it_line_coverage'],
+  'new_it_uncovered_conditions': ['new_it_branch_coverage']
+};
index 80793a9550ff4a98a6fbe74a046560183f81b0a3..c9df13a2482ed701bd7294cdabb5e5fdced76be0 100644 (file)
@@ -21,23 +21,45 @@ import React from 'react';
 
 import ComponentsListRow from './ComponentsListRow';
 import EmptyComponentsList from './EmptyComponentsList';
+import complementary from '../../config/complementary';
 
-const ComponentsList = ({ components, selected, metric, onClick }) => {
+const ComponentsList = ({ components, metrics, selected, metric, onClick }) => {
   if (!components.length) {
     return <EmptyComponentsList/>;
   }
 
+  const otherMetrics = (complementary[metric.key] || []).map(metric => {
+    return metrics.find(m => m.key === metric);
+  });
+
   return (
       <table className="data zebra zebra-hover">
+        {otherMetrics.length > 0 && (
+            <thead>
+              <tr>
+                <th>&nbsp;</th>
+                <th className="text-right">
+                  <span className="small">{metric.name}</span>
+                </th>
+                {otherMetrics.map(metric => (
+                    <th key={metric.key} className="text-right">
+                      <span className="small">{metric.name}</span>
+                    </th>
+                ))}
+              </tr>
+            </thead>
+        )}
+
         <tbody>
-        {components.map(component => (
-            <ComponentsListRow
-                key={component.id}
-                component={component}
-                isSelected={component === selected}
-                metric={metric}
-                onClick={onClick}/>
-        ))}
+          {components.map(component => (
+              <ComponentsListRow
+                  key={component.id}
+                  component={component}
+                  otherMetrics={otherMetrics}
+                  isSelected={component === selected}
+                  metric={metric}
+                  onClick={onClick}/>
+          ))}
         </tbody>
       </table>
   );
index 48c871cfac1d9c501f4105c714d2442b4a4c4432..d734aca5f758420602d8e80753c2686562100c1a 100644 (file)
@@ -22,11 +22,24 @@ import React from 'react';
 import ComponentCell from './ComponentCell';
 import MeasureCell from './MeasureCell';
 
-const ComponentsListRow = ({ component, isSelected, metric, onClick }) => {
+const replaceMeasure = (component, measure) => {
+  return {
+    ...component,
+    value: measure.value,
+    leak: measure.leak
+  };
+};
+
+const ComponentsListRow = ({ component, otherMetrics, isSelected, metric, onClick }) => {
   const handleClick = () => {
     onClick(component);
   };
 
+  const otherMeasures = otherMetrics.map(metric => {
+    const measure = component.measures.find(measure => measure.metric === metric.key);
+    return { ...measure, metric };
+  });
+
   return (
       <tr>
         <ComponentCell
@@ -37,6 +50,13 @@ const ComponentsListRow = ({ component, isSelected, metric, onClick }) => {
         <MeasureCell
             component={component}
             metric={metric}/>
+
+        {otherMeasures.map(measure => (
+            <MeasureCell
+                key={measure.metric.key}
+                component={replaceMeasure(component, measure)}
+                metric={measure.metric}/>
+        ))}
       </tr>
   );
 };
index 03484927b0a271b38e8f74799f55def109c649eb..789ae2ac494c2c50b6b8f2e72aa8d3b798075069 100644 (file)
@@ -87,7 +87,7 @@ export default class ListView extends React.Component {
   }
 
   render () {
-    const { component, components, metric, leakPeriod, selected, fetching, total } = this.props;
+    const { component, components, metrics, metric, leakPeriod, selected, fetching, total } = this.props;
     const { onSelectNext, onSelectPrevious } = this.props;
 
     const breadcrumbs = [component];
@@ -113,6 +113,7 @@ export default class ListView extends React.Component {
                 {(!fetching || components.length !== 0) ? (
                     <ComponentsList
                         components={components}
+                        metrics={metrics}
                         selected={selected}
                         metric={metric}
                         onClick={this.handleClick.bind(this)}/>
index 6759d25be95f156c794342862b17fc719062bb90..1d789b5c88757fed8057874e2fa0c99f439e5159 100644 (file)
@@ -33,6 +33,7 @@ const mapStateToProps = state => {
   return {
     ...drilldown,
     component: state.app.component,
+    metrics: state.app.metrics,
     metric: state.details.metric,
     fetching: state.status.fetching
   };
index 2b030cc6feb1c1a8f6f5deb706a2e98a15be9563..bed973fa11f84f40c522cd0b9eeb2b46bee44b92 100644 (file)
@@ -24,7 +24,7 @@ import Measure from '../../components/Measure';
 const MeasureCell = ({ component, metric }) => {
   return (
       <td className="thin nowrap text-right">
-        <span id={'component-measures-component-measure-' + component.key}>
+        <span id={'component-measures-component-measure-' + component.key + '-' + metric.key}>
           <Measure measure={{ value: component.value, leak: component.leak }} metric={metric}/>
         </span>
       </td>
index 47fd5dbaf895017017c448752378faa00ba0a0dd..c4e62fdccb368c47dac5d58350fda7ccac17e6ce 100644 (file)
@@ -89,7 +89,7 @@ export default class TreeView extends React.Component {
   }
 
   render () {
-    const { components, breadcrumbs, metric, leakPeriod, selected, fetching, total } = this.props;
+    const { components, metrics, breadcrumbs, metric, leakPeriod, selected, fetching, total } = this.props;
     const { onSelectNext, onSelectPrevious, onFetchMore } = this.props;
 
     const selectedIndex = components.indexOf(selected);
@@ -111,6 +111,7 @@ export default class TreeView extends React.Component {
                 {(!fetching || components.length !== 0) ? (
                     <ComponentsList
                         components={components}
+                        metrics={metrics}
                         selected={selected}
                         metric={metric}
                         onClick={this.handleClick.bind(this)}/>
index 5643effcbc362fbdb496c533b54eff98f6586047..b707563d12b332fff1d271068ed161aa8c600cd3 100644 (file)
@@ -42,6 +42,7 @@ const mapStateToProps = state => {
   return {
     ...drilldown,
     component: state.app.component,
+    metrics: state.app.metrics,
     metric: state.details.metric,
     fetching: state.status.fetching
   };
index a10dad931f8f4660ebb431f31912868f890789f9..17b1d63cd54916bc9cd7905a09792ed3244dfcd8 100644 (file)
@@ -18,8 +18,9 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import { getComponentTree } from '../../../api/components';
-import { enhanceWithSingleMeasure } from '../utils';
+import { enhanceWithMeasure } from '../utils';
 import { startFetching, stopFetching } from './statusActions';
+import complementary from '../config/complementary';
 
 export const UPDATE_STORE = 'drilldown/list/UPDATE_STORE';
 
@@ -27,6 +28,11 @@ function updateStore (state) {
   return { type: UPDATE_STORE, state };
 }
 
+function getComplementary (metric) {
+  const comp = complementary[metric] || [];
+  return [metric, ...comp];
+}
+
 function makeRequest (baseComponent, metric, options, periodIndex = 1) {
   const asc = metric.direction === 1;
   const ps = 100;
@@ -46,14 +52,14 @@ function makeRequest (baseComponent, metric, options, periodIndex = 1) {
   }
 
   Object.assign(finalOptions, options);
-  return getComponentTree('leaves', baseComponent.key, [metric.key], finalOptions);
+  return getComponentTree('leaves', baseComponent.key, getComplementary(metric.key), finalOptions);
 }
 
 function fetchLeaves (baseComponent, metric, pageIndex = 1, periodIndex = 1) {
   const options = { p: pageIndex };
 
   return makeRequest(baseComponent, metric, options, periodIndex).then(r => {
-    const nextComponents = enhanceWithSingleMeasure(r.components, periodIndex);
+    const nextComponents = enhanceWithMeasure(r.components, metric.key, periodIndex);
 
     return {
       components: nextComponents,
index 84681697821712bdb13e2ccad70d588289790011..32253d909c81dc446b5007f27980130c3571942a 100644 (file)
@@ -20,8 +20,9 @@
 import initial from 'lodash/initial';
 
 import { getComponentTree } from '../../../api/components';
-import { enhanceWithSingleMeasure } from '../utils';
+import { enhanceWithMeasure } from '../utils';
 import { startFetching, stopFetching } from './statusActions';
+import complementary from '../config/complementary';
 
 /*
  * Actions
@@ -59,6 +60,11 @@ function init (rootComponent, metric, periodIndex = 1) {
  * Workflow
  */
 
+function getComplementary (metric) {
+  const comp = complementary[metric] || [];
+  return [metric, ...comp];
+}
+
 function makeRequest (baseComponent, metric, options, periodIndex = 1) {
   const asc = metric.direction === 1;
   const ps = 100;
@@ -78,14 +84,14 @@ function makeRequest (baseComponent, metric, options, periodIndex = 1) {
   }
 
   Object.assign(finalOptions, options);
-  return getComponentTree('children', baseComponent.key, [metric.key], finalOptions);
+  return getComponentTree('children', baseComponent.key, getComplementary(metric.key), finalOptions);
 }
 
 function fetchComponents (baseComponent, metric, pageIndex = 1, periodIndex = 1) {
   const options = { p: pageIndex };
 
   return makeRequest(baseComponent, metric, options, periodIndex).then(r => {
-    const nextComponents = enhanceWithSingleMeasure(r.components, periodIndex);
+    const nextComponents = enhanceWithMeasure(r.components, metric.key, periodIndex);
 
     return {
       baseComponent,
index 6a35c90f8d600527bd1e36506010f205fdae6f43..5a6db31f29e8e0488213c07771bf23c15a0c9fba 100644 (file)
@@ -87,6 +87,17 @@ export function enhanceWithSingleMeasure (components, periodIndex = 1) {
       });
 }
 
+export function enhanceWithMeasure (components, metric, periodIndex = 1) {
+  return components
+      .map(component => {
+        const measuresWithLeak = enhanceWithLeak(component.measures, periodIndex);
+        const measure = measuresWithLeak.find(measure => measure.metric === metric);
+        const value = measure ? measure.value : null;
+        const leak = measure ? measure.leak : null;
+        return { ...component, value, leak, measures: measuresWithLeak };
+      });
+}
+
 export function hasHistory (metricKey) {
   return metricKey.indexOf('new_') !== 0;
 }