]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10244 Warning message should disappear when first analysis is in progress
authorStas Vilchik <stas.vilchik@sonarsource.com>
Wed, 10 Jan 2018 16:43:55 +0000 (17:43 +0100)
committerStas Vilchik <stas.vilchik@sonarsource.com>
Fri, 12 Jan 2018 16:37:53 +0000 (17:37 +0100)
server/sonar-web/src/main/js/app/components/ComponentContainer.tsx
server/sonar-web/src/main/js/app/components/__tests__/ComponentContainer-test.tsx
server/sonar-web/src/main/js/app/components/nav/component/ComponentNav.tsx
server/sonar-web/src/main/js/app/components/nav/component/__tests__/ComponentNav-test.tsx
server/sonar-web/src/main/js/app/components/nav/component/__tests__/__snapshots__/ComponentNav-test.tsx.snap
server/sonar-web/src/main/js/apps/overview/components/App.js
server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.js [deleted file]
server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/overview/components/__tests__/EmptyOverview-test.js [deleted file]
server/sonar-web/src/main/js/apps/overview/components/__tests__/EmptyOverview-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/EmptyOverview-test.tsx.snap [new file with mode: 0644]

index eb02ba1d579e48729a054ac7adaf90c1a4b1ade9..4e6a5c1ee97cc0ae49295ce4bef82783eee4ced2 100644 (file)
@@ -24,10 +24,12 @@ import ComponentNav from './nav/component/ComponentNav';
 import { Branch, Component } from '../types';
 import handleRequiredAuthorization from '../utils/handleRequiredAuthorization';
 import { getBranches } from '../../api/branches';
+import { Task, getTasksForComponent } from '../../api/ce';
 import { getComponentData } from '../../api/components';
 import { getComponentNavigation } from '../../api/nav';
 import { fetchOrganizations } from '../../store/rootActions';
 import { areThereCustomOrganizations } from '../../store/rootReducer';
+import { STATUSES } from '../../apps/background-tasks/constants';
 
 interface Props {
   children: any;
@@ -42,6 +44,9 @@ interface State {
   branches: Branch[];
   loading: boolean;
   component: Component | null;
+  currentTask?: Task;
+  isInProgress?: boolean;
+  isPending?: boolean;
 }
 
 export class ComponentContainer extends React.PureComponent<Props, State> {
@@ -102,6 +107,8 @@ export class ComponentContainer extends React.PureComponent<Props, State> {
             this.setState({ loading: false, branches, component });
           }
         }, onError);
+
+        this.fetchStatus(component);
       },
       onError
     );
@@ -112,6 +119,21 @@ export class ComponentContainer extends React.PureComponent<Props, State> {
     return project ? getBranches(project.key) : Promise.resolve([]);
   };
 
+  fetchStatus = (component: Component) => {
+    getTasksForComponent(component.key).then(
+      ({ current, queue }) => {
+        if (this.mounted) {
+          this.setState({
+            currentTask: current,
+            isInProgress: queue.some(task => task.status === STATUSES.IN_PROGRESS),
+            isPending: queue.some(task => task.status === STATUSES.PENDING)
+          });
+        }
+      },
+      () => {}
+    );
+  };
+
   handleComponentChange = (changes: {}) => {
     if (this.mounted) {
       this.setState(state => ({ component: { ...state.component, ...changes } }));
@@ -149,6 +171,9 @@ export class ComponentContainer extends React.PureComponent<Props, State> {
               branches={branches}
               currentBranch={branch}
               component={component}
+              currentTask={this.state.currentTask}
+              isInProgress={this.state.isInProgress}
+              isPending={this.state.isPending}
               location={this.props.location}
             />
           )}
@@ -161,6 +186,8 @@ export class ComponentContainer extends React.PureComponent<Props, State> {
             branch,
             branches,
             component,
+            isInProgress: this.state.isInProgress,
+            isPending: this.state.isPending,
             onBranchesChange: this.handleBranchesChange,
             onComponentChange: this.handleComponentChange
           })
index 1ba8962fcbb315f384b0fba47e79676a1cbec5d7..9fc7e3d7925c99d8be94a21fdb69749bc79124f8 100644 (file)
@@ -21,17 +21,22 @@ import * as React from 'react';
 import { shallow, mount } from 'enzyme';
 import { ComponentContainer } from '../ComponentContainer';
 import { getBranches } from '../../../api/branches';
+import { getTasksForComponent } from '../../../api/ce';
 import { getComponentData } from '../../../api/components';
 import { getComponentNavigation } from '../../../api/nav';
 
 jest.mock('../../../api/branches', () => ({ getBranches: jest.fn(() => Promise.resolve([])) }));
+jest.mock('../../../api/ce', () => ({
+  getTasksForComponent: jest.fn(() => Promise.resolve({ queue: [] }))
+}));
 jest.mock('../../../api/components', () => ({
   getComponentData: jest.fn(() => Promise.resolve({}))
 }));
 jest.mock('../../../api/nav', () => ({
   getComponentNavigation: jest.fn(() =>
     Promise.resolve({
-      breadcrumbs: [{ key: 'portfolioKey', name: 'portfolio', qualifier: 'VW' }]
+      breadcrumbs: [{ key: 'portfolioKey', name: 'portfolio', qualifier: 'VW' }],
+      key: 'portfolioKey'
     })
   )
 }));
@@ -47,6 +52,7 @@ beforeEach(() => {
   (getBranches as jest.Mock<any>).mockClear();
   (getComponentData as jest.Mock<any>).mockClear();
   (getComponentNavigation as jest.Mock<any>).mockClear();
+  (getTasksForComponent as jest.Mock<any>).mockClear();
 });
 
 it('changes component', () => {
@@ -137,3 +143,21 @@ it('loads organization', async () => {
   await new Promise(setImmediate);
   expect(fetchOrganizations).toBeCalledWith(['org']);
 });
+
+it('fetches status', async () => {
+  (getComponentData as jest.Mock<any>).mockImplementationOnce(() =>
+    Promise.resolve({ organization: 'org' })
+  );
+
+  mount(
+    <ComponentContainer
+      fetchOrganizations={jest.fn()}
+      location={{ query: { id: 'foo' } }}
+      organizationsEnabled={true}>
+      <Inner />
+    </ComponentContainer>
+  );
+
+  await new Promise(setImmediate);
+  expect(getTasksForComponent).toBeCalledWith('portfolioKey');
+});
index 39925c34c99fd04073d541aaa675f76adba905ee..727d2f19d5f41a19f30414ef4dc29295e6daed15 100644 (file)
@@ -26,7 +26,7 @@ import RecentHistory from '../../RecentHistory';
 import * as theme from '../../../theme';
 import { Branch, Component } from '../../../types';
 import ContextNavBar from '../../../../components/nav/ContextNavBar';
-import { getTasksForComponent, PendingTask, Task } from '../../../../api/ce';
+import { Task } from '../../../../api/ce';
 import { STATUSES } from '../../../../apps/background-tasks/constants';
 import './ComponentNav.css';
 
@@ -34,52 +34,25 @@ interface Props {
   branches: Branch[];
   currentBranch?: Branch;
   component: Component;
-  location: {};
-}
-
-interface State {
   currentTask?: Task;
   isInProgress?: boolean;
   isPending?: boolean;
+  location: {};
 }
 
-export default class ComponentNav extends React.PureComponent<Props, State> {
+export default class ComponentNav extends React.PureComponent<Props> {
   mounted: boolean;
 
-  state: State = {};
-
   componentDidMount() {
-    this.mounted = true;
-    this.loadStatus();
     this.populateRecentHistory();
   }
 
   componentDidUpdate(prevProps: Props) {
     if (this.props.component.key !== prevProps.component.key) {
-      this.loadStatus();
       this.populateRecentHistory();
     }
   }
 
-  componentWillUnmount() {
-    this.mounted = false;
-  }
-
-  loadStatus = () => {
-    getTasksForComponent(this.props.component.key).then(
-      (r: { queue: PendingTask[]; current: Task }) => {
-        if (this.mounted) {
-          this.setState({
-            currentTask: r.current,
-            isInProgress: r.queue.some(task => task.status === STATUSES.IN_PROGRESS),
-            isPending: r.queue.some(task => task.status === STATUSES.PENDING)
-          });
-        }
-      },
-      () => {}
-    );
-  };
-
   populateRecentHistory = () => {
     const { breadcrumbs } = this.props.component;
     const { qualifier } = breadcrumbs[breadcrumbs.length - 1];
@@ -94,7 +67,7 @@ export default class ComponentNav extends React.PureComponent<Props, State> {
   };
 
   render() {
-    const { currentTask, isInProgress, isPending } = this.state;
+    const { currentTask, isInProgress, isPending } = this.props;
     let notifComponent;
     if (isInProgress || isPending || (currentTask && currentTask.status === STATUSES.FAILED)) {
       notifComponent = (
index 4c30ef3b3a495f50f5dea049a1c2411f1dfb7448..69d57e25d67769338b87d03f450aef2422482629 100644 (file)
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-/* eslint-disable import/order */
 import * as React from 'react';
-import { mount, shallow } from 'enzyme';
+import { shallow } from 'enzyme';
 import ComponentNav from '../ComponentNav';
 
-jest.mock('../ComponentNavMeta', () => ({
-  // eslint-disable-next-line
-  default: function ComponentNavMeta() {
-    return null;
-  }
-}));
-
-jest.mock('../ComponentNavHeader', () => ({
-  // eslint-disable-next-line
-  default: function ComponentNavHeader() {
-    return null;
-  }
-}));
-
-jest.mock('../ComponentNavMenu', () => ({
-  // eslint-disable-next-line
-  default: function ComponentNavMenu() {
-    return null;
-  }
-}));
-
-jest.mock('../../../RecentHistory', () => ({
-  default: { add: jest.fn() }
-}));
-
-jest.mock('../../../../../api/ce', () => ({
-  getTasksForComponent: jest.fn(() => Promise.resolve({ queue: [] }))
-}));
-
-const getTasksForComponent = require('../../../../../api/ce').getTasksForComponent as jest.Mock<
-  any
->;
-
 const component = {
   breadcrumbs: [{ key: 'component', name: 'component', qualifier: 'TRK' }],
   key: 'component',
@@ -63,12 +29,6 @@ const component = {
   qualifier: 'TRK'
 };
 
-it('loads status', () => {
-  getTasksForComponent.mockClear();
-  mount(<ComponentNav branches={[]} component={component} location={{}} />);
-  expect(getTasksForComponent).toBeCalledWith('component');
-});
-
 it('renders', () => {
   const wrapper = shallow(<ComponentNav branches={[]} component={component} location={{}} />);
   wrapper.setState({ isInProgress: true, isPending: true });
index 25470b77544a41611005a719b512f1f2a6156272..e52606979cfb2781bd7e8b3726700290cd6c4dd0 100644 (file)
@@ -2,32 +2,10 @@
 
 exports[`renders 1`] = `
 <ContextNavBar
-  height={92}
+  height={72}
   id="context-navigation"
-  notif={
-    <ComponentNavBgTaskNotif
-      component={
-        Object {
-          "breadcrumbs": Array [
-            Object {
-              "key": "component",
-              "name": "component",
-              "qualifier": "TRK",
-            },
-          ],
-          "key": "component",
-          "name": "component",
-          "organization": "org",
-          "qualifier": "TRK",
-        }
-      }
-      currentTask={undefined}
-      isInProgress={true}
-      isPending={true}
-    />
-  }
 >
-  <ComponentNavHeader
+  <Connect(ComponentNavHeader)
     branches={Array []}
     component={
       Object {
@@ -46,7 +24,7 @@ exports[`renders 1`] = `
     }
     location={Object {}}
   />
-  <ComponentNavMeta
+  <Connect(ComponentNavMeta)
     component={
       Object {
         "breadcrumbs": Array [
index ae0a02d6f6273bbfb03b322dcb1a77696f80e679..2d4067baa60782a49c085e8ccb3812dd84f60ea0 100644 (file)
@@ -37,6 +37,8 @@ type Props = {
     tags: Array<string>,
     organization?: string
   },
+  isInProgress?: bool,
+  isPending?: bool,
   onComponentChange: {} => void,
   router: Object
 };
@@ -83,7 +85,12 @@ export default class App extends React.PureComponent {
     }
 
     if (!component.analysisDate) {
-      return <EmptyOverview component={component} />;
+      return (
+        <EmptyOverview
+          component={component.key}
+          showWarning={!this.props.isPending && !this.props.isInProgress}
+        />
+      );
     }
 
     return (
diff --git a/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.js b/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.js
deleted file mode 100644 (file)
index 634d019..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 React from 'react';
-import { Link } from 'react-router';
-import { translate } from '../../../helpers/l10n';
-
-/*::
-type Props = {
-  component: { key: string }
-};
-*/
-
-export default function EmptyOverview({ component } /*: Props */) {
-  const rawMessage = translate('provisioning.no_analysis.delete');
-  const head = rawMessage.substr(0, rawMessage.indexOf('{0}'));
-  const tail = rawMessage.substr(rawMessage.indexOf('{0}') + 3);
-
-  return (
-    <div className="page page-limited">
-      <div className="alert alert-warning">{translate('provisioning.no_analysis')}</div>
-
-      <div className="big-spacer-top">
-        {head}
-        <Link
-          className="text-danger"
-          to={{ pathname: '/project/deletion', query: { id: component.key } }}>
-          {translate('provisioning.no_analysis.delete_project')}
-        </Link>
-        {tail}
-      </div>
-
-      <div className="big-spacer-top">
-        <h4>{translate('key')}</h4>
-        <code>{component.key}</code>
-      </div>
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx b/server/sonar-web/src/main/js/apps/overview/components/EmptyOverview.tsx
new file mode 100644 (file)
index 0000000..0dd1b07
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 * as React from 'react';
+import { Link } from 'react-router';
+import { translate } from '../../../helpers/l10n';
+
+interface Props {
+  component: string;
+  showWarning?: boolean;
+}
+
+export default function EmptyOverview({ component, showWarning }: Props) {
+  const rawMessage = translate('provisioning.no_analysis.delete');
+  const head = rawMessage.substr(0, rawMessage.indexOf('{0}'));
+  const tail = rawMessage.substr(rawMessage.indexOf('{0}') + 3);
+
+  return (
+    <div className="page page-limited">
+      {showWarning && (
+        <div className="big-spacer-bottom">
+          <div className="alert alert-warning">{translate('provisioning.no_analysis')}</div>
+
+          <div className="big-spacer-top">
+            {head}
+            <Link
+              className="text-danger"
+              to={{ pathname: '/project/deletion', query: { id: component } }}>
+              {translate('provisioning.no_analysis.delete_project')}
+            </Link>
+            {tail}
+          </div>
+        </div>
+      )}
+
+      <div>
+        <h4>{translate('key')}</h4>
+        <code>{component}</code>
+      </div>
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/EmptyOverview-test.js b/server/sonar-web/src/main/js/apps/overview/components/__tests__/EmptyOverview-test.js
deleted file mode 100644 (file)
index 7dc5868..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2018 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 EmptyOverview from '../EmptyOverview';
-
-it('should render component key', () => {
-  const component = {
-    id: 'id',
-    key: 'abcd',
-    analysisDate: '2016-01-01'
-  };
-  const output = shallow(<EmptyOverview component={component} />);
-  expect(output.find('code').text()).toBe('abcd');
-});
diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/EmptyOverview-test.tsx b/server/sonar-web/src/main/js/apps/overview/components/__tests__/EmptyOverview-test.tsx
new file mode 100644 (file)
index 0000000..eeee97a
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2018 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 * as React from 'react';
+import { shallow } from 'enzyme';
+import EmptyOverview from '../EmptyOverview';
+
+it('renders', () => {
+  expect(shallow(<EmptyOverview component="abcd" />)).toMatchSnapshot();
+});
+
+it('does not render warning', () => {
+  expect(shallow(<EmptyOverview component="abcd" showWarning={false} />)).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/EmptyOverview-test.tsx.snap b/server/sonar-web/src/main/js/apps/overview/components/__tests__/__snapshots__/EmptyOverview-test.tsx.snap
new file mode 100644 (file)
index 0000000..824034a
--- /dev/null
@@ -0,0 +1,31 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`does not render warning 1`] = `
+<div
+  className="page page-limited"
+>
+  <div>
+    <h4>
+      key
+    </h4>
+    <code>
+      abcd
+    </code>
+  </div>
+</div>
+`;
+
+exports[`renders 1`] = `
+<div
+  className="page page-limited"
+>
+  <div>
+    <h4>
+      key
+    </h4>
+    <code>
+      abcd
+    </code>
+  </div>
+</div>
+`;