]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-9833 'A' ratings when a portfolio without project is analyzed
authorStas Vilchik <stas.vilchik@sonarsource.com>
Mon, 18 Sep 2017 11:25:28 +0000 (13:25 +0200)
committerTeryk Bellahsene <teryk@users.noreply.github.com>
Wed, 20 Sep 2017 07:15:23 +0000 (09:15 +0200)
server/sonar-web/src/main/js/apps/portfolio/components/Activity.tsx
server/sonar-web/src/main/js/apps/portfolio/components/App.tsx
server/sonar-web/src/main/js/apps/portfolio/components/Report.tsx
server/sonar-web/src/main/js/apps/portfolio/components/Summary.tsx
server/sonar-web/src/main/js/apps/portfolio/components/__tests__/App-test.tsx
server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Activity-test.tsx.snap
server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/App-test.tsx.snap
server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Report-test.tsx.snap
server/sonar-web/src/main/js/apps/portfolio/components/__tests__/__snapshots__/Summary-test.tsx.snap
server/sonar-web/src/main/less/init/misc.less
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 26a66872a0193a10b1f44b54b69c03f77da7aef9..63ccdd7217cb80838f087b42bdb98d2b9c9c625a 100644 (file)
@@ -97,7 +97,7 @@ export default class Activity extends React.PureComponent<Props> {
 
   render() {
     return (
-      <div className="huge-spacer-top">
+      <div className="huge-spacer-bottom">
         <header className="page-header">
           <h3 className="page-title">{translate('project_activity.page')}</h3>
         </header>
index e0910baa420324854509f8839b37e7f1b17e128c..ec7f35fc0b2efaba65c66c13ea09459d119da320 100644 (file)
@@ -31,6 +31,7 @@ import { getChildren } from '../../../api/components';
 import { PORTFOLIO_METRICS, SUB_COMPONENTS_METRICS, convertMeasures } from '../utils';
 import { SubComponent } from '../types';
 import '../styles.css';
+import { translate } from '../../../helpers/l10n';
 
 interface Props {
   component: { key: string; name: string };
@@ -97,6 +98,11 @@ export default class App extends React.PureComponent<Props, State> {
     );
   }
 
+  isEmpty = () => this.state.measures == undefined || this.state.measures['ncloc'] == undefined;
+
+  isNotComputed = () =>
+    this.state.measures && this.state.measures['reliability_rating'] == undefined;
+
   renderSpinner() {
     return (
       <div className="page page-limited">
@@ -107,9 +113,59 @@ export default class App extends React.PureComponent<Props, State> {
     );
   }
 
+  renderEmpty() {
+    return (
+      <div className="empty-search">
+        <h3>{translate('portfolio.empty')}</h3>
+        <p>{translate('portfolio.empty.hint')}</p>
+      </div>
+    );
+  }
+
+  renderWhenNotComputed() {
+    return (
+      <div className="empty-search">
+        <h3>{translate('portfolio.not_computed')}</h3>
+      </div>
+    );
+  }
+
+  renderMain() {
+    const { component } = this.props;
+    const { measures, subComponents, totalSubComponents } = this.state;
+
+    if (this.isEmpty()) {
+      return this.renderEmpty();
+    }
+
+    if (this.isNotComputed()) {
+      return this.renderWhenNotComputed();
+    }
+
+    return (
+      <div>
+        <div className="portfolio-boxes">
+          <ReleasabilityBox component={component.key} measures={measures!} />
+          <ReliabilityBox component={component.key} measures={measures!} />
+          <SecurityBox component={component.key} measures={measures!} />
+          <MaintainabilityBox component={component.key} measures={measures!} />
+        </div>
+
+        {subComponents != undefined &&
+        totalSubComponents != undefined && (
+          <WorstProjects
+            component={component.key}
+            subComponents={subComponents}
+            total={totalSubComponents}
+          />
+        )}
+      </div>
+    );
+  }
+
   render() {
     const { component } = this.props;
-    const { loading, measures, subComponents, totalSubComponents } = this.state;
+    const { loading, measures } = this.state;
 
     if (loading) {
       return this.renderSpinner();
@@ -118,28 +174,11 @@ export default class App extends React.PureComponent<Props, State> {
     return (
       <div className="page page-limited">
         <div className="page-with-sidebar">
-          <div className="page-main">
-            {measures != undefined && (
-              <div className="portfolio-boxes">
-                <ReleasabilityBox component={component.key} measures={measures} />
-                <ReliabilityBox component={component.key} measures={measures} />
-                <SecurityBox component={component.key} measures={measures} />
-                <MaintainabilityBox component={component.key} measures={measures} />
-              </div>
-            )}
-
-            {subComponents != undefined &&
-            totalSubComponents != undefined && (
-              <WorstProjects
-                component={component.key}
-                subComponents={subComponents}
-                total={totalSubComponents}
-              />
-            )}
-          </div>
+          <div className="page-main">{this.renderMain()}</div>
 
           <aside className="page-sidebar-fixed">
-            {measures != undefined && <Summary component={component} measures={measures} />}
+            {!this.isEmpty() &&
+            !this.isNotComputed() && <Summary component={component} measures={measures!} />}
             <Activity component={component.key} />
             <Report component={component} />
           </aside>
index 596afcba9731c246750ccdedceaf6e2988007419..86bf16d5050d21bfb67ca3cc15921445218711f2 100644 (file)
@@ -71,7 +71,7 @@ export default class Report extends React.PureComponent<Props, State> {
 
     if (loading) {
       return (
-        <div className="huge-spacer-top">
+        <div>
           {this.renderHeader()}
           <i className="spinner" />
         </div>
@@ -83,7 +83,7 @@ export default class Report extends React.PureComponent<Props, State> {
     }
 
     return (
-      <div className="huge-spacer-top">
+      <div>
         {this.renderHeader()}
 
         {!status.canDownload && (
index ef13cf58cb87260cf8203f69212e775829c72797..aa229597a1ee7dd0c6ad93bb29e8f8d6afea5eda 100644 (file)
@@ -35,7 +35,7 @@ export default function Summary({ component, measures }: Props) {
   const nclocDistribution = measures['ncloc_language_distribution'];
 
   return (
-    <section id="portfolio-summary" className="portfolio-section portfolio-section-summary">
+    <section id="portfolio-summary" className="huge-spacer-bottom">
       {component.description && <div className="big-spacer-bottom">{component.description}</div>}
 
       <ul className="portfolio-grid">
index fafff182895270589dea62105ac9328557a2539d..e9c93fb7279ec5c75446815a7ac2d4d21ced2c98 100644 (file)
@@ -49,7 +49,24 @@ const component = { key: 'foo', name: 'Foo' };
 
 it('renders', () => {
   const wrapper = shallow(<App component={component} />);
-  wrapper.setState({ loading: false, measures: {}, subComponents: [], totalSubComponents: 0 });
+  wrapper.setState({
+    loading: false,
+    measures: { ncloc: '173', reliability_rating: '1' },
+    subComponents: [],
+    totalSubComponents: 0
+  });
+  expect(wrapper).toMatchSnapshot();
+});
+
+it('renders when portfolio is empty', () => {
+  const wrapper = shallow(<App component={component} />);
+  wrapper.setState({ loading: false, measures: { reliability_rating: '1' } });
+  expect(wrapper).toMatchSnapshot();
+});
+
+it('renders when portfolio is not computed', () => {
+  const wrapper = shallow(<App component={component} />);
+  wrapper.setState({ loading: false, measures: { ncloc: '173' } });
   expect(wrapper).toMatchSnapshot();
 });
 
index 1df37032691d8eca6d58446d4d4dbf4549098c4f..fa3361a98b5995caffa9b98e8a923a76308e06d4 100644 (file)
@@ -2,7 +2,7 @@
 
 exports[`renders 1`] = `
 <div
-  className="huge-spacer-top"
+  className="huge-spacer-bottom"
 >
   <header
     className="page-header"
index 4a1077741b9cb3b6f9ca55e09e19deca3bdb0796..2e26c3cc408c12e8fba384de13b228af00cf2070 100644 (file)
@@ -10,44 +10,148 @@ exports[`renders 1`] = `
     <div
       className="page-main"
     >
-      <div
-        className="portfolio-boxes"
-      >
-        <ReleasabilityBox
-          component="foo"
-          measures={Object {}}
-        />
-        <ReliabilityBox
-          component="foo"
-          measures={Object {}}
-        />
-        <SecurityBox
-          component="foo"
-          measures={Object {}}
-        />
-        <MaintainabilityBox
+      <div>
+        <div
+          className="portfolio-boxes"
+        >
+          <ReleasabilityBox
+            component="foo"
+            measures={
+              Object {
+                "ncloc": "173",
+                "reliability_rating": "1",
+              }
+            }
+          />
+          <ReliabilityBox
+            component="foo"
+            measures={
+              Object {
+                "ncloc": "173",
+                "reliability_rating": "1",
+              }
+            }
+          />
+          <SecurityBox
+            component="foo"
+            measures={
+              Object {
+                "ncloc": "173",
+                "reliability_rating": "1",
+              }
+            }
+          />
+          <MaintainabilityBox
+            component="foo"
+            measures={
+              Object {
+                "ncloc": "173",
+                "reliability_rating": "1",
+              }
+            }
+          />
+        </div>
+        <WorstProjects
           component="foo"
-          measures={Object {}}
+          subComponents={Array []}
+          total={0}
         />
       </div>
-      <WorstProjects
+    </div>
+    <aside
+      className="page-sidebar-fixed"
+    >
+      <Summary
+        component={
+          Object {
+            "key": "foo",
+            "name": "Foo",
+          }
+        }
+        measures={
+          Object {
+            "ncloc": "173",
+            "reliability_rating": "1",
+          }
+        }
+      />
+      <Activity
         component="foo"
-        subComponents={Array []}
-        total={0}
       />
+      <Report
+        component={
+          Object {
+            "key": "foo",
+            "name": "Foo",
+          }
+        }
+      />
+    </aside>
+  </div>
+</div>
+`;
+
+exports[`renders when portfolio is empty 1`] = `
+<div
+  className="page page-limited"
+>
+  <div
+    className="page-with-sidebar"
+  >
+    <div
+      className="page-main"
+    >
+      <div
+        className="empty-search"
+      >
+        <h3>
+          portfolio.empty
+        </h3>
+        <p>
+          portfolio.empty.hint
+        </p>
+      </div>
     </div>
     <aside
       className="page-sidebar-fixed"
     >
-      <Summary
+      <Activity
+        component="foo"
+      />
+      <Report
         component={
           Object {
             "key": "foo",
             "name": "Foo",
           }
         }
-        measures={Object {}}
       />
+    </aside>
+  </div>
+</div>
+`;
+
+exports[`renders when portfolio is not computed 1`] = `
+<div
+  className="page page-limited"
+>
+  <div
+    className="page-with-sidebar"
+  >
+    <div
+      className="page-main"
+    >
+      <div
+        className="empty-search"
+      >
+        <h3>
+          portfolio.not_computed
+        </h3>
+      </div>
+    </div>
+    <aside
+      className="page-sidebar-fixed"
+    >
       <Activity
         component="foo"
       />
index 76b6a4056ab64f4093600ba2a2e84c40f37b1844..50b579f8303ecb46be6599b79f422cedfb1f6eb1 100644 (file)
@@ -1,9 +1,7 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
 exports[`renders 1`] = `
-<div
-  className="huge-spacer-top"
->
+<div>
   <header
     className="page-header"
   >
@@ -20,9 +18,7 @@ exports[`renders 1`] = `
 `;
 
 exports[`renders 2`] = `
-<div
-  className="huge-spacer-top"
->
+<div>
   <header
     className="page-header"
   >
index 8f934778b8dca62b80314e3962ee791511fb19d8..d377a6a44a9ca8cf1c89ee509928e16f9d8ac541 100644 (file)
@@ -2,7 +2,7 @@
 
 exports[`renders 1`] = `
 <section
-  className="portfolio-section portfolio-section-summary"
+  className="huge-spacer-bottom"
   id="portfolio-summary"
 >
   <div
index ddca44d3771d36b2fb62cde6dadad6edc62b5d36..d83fb0b515342c03fc5578d798e4509caea264f6 100644 (file)
@@ -83,6 +83,9 @@ th.nowrap {
 .huge-spacer-top {
   margin-top: 40px;
 }
+.huge-spacer-bottom {
+  margin-bottom: 40px;
+}
 .huge-spacer-left {
   margin-left: 40px;
 }
index 4248cbb0235b2b8518afeea9f1c6b81157c799d5..68f658bbffa39372cc2e243d6f438cb8a9c1de81 100644 (file)
@@ -3194,3 +3194,6 @@ branches.settings_hint_tab=Administration > Branches
 #------------------------------------------------------------------------------
 portfolio.was_x_y=was {rating} {date}
 portfolio.x_in_y={projects} in {rating}
+portfolio.empty=This portfolio is empty.
+portfolio.empty.hint=This portfolio has no projects, or none of associated projects has lines of code.
+portfolio.not_computed=This portfolio is not yet computed.