]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-13342 Fix broken projects links in portfolios
authorJeremy Davis <jeremy.davis@sonarsource.com>
Thu, 30 Apr 2020 09:40:31 +0000 (11:40 +0200)
committersonartech <sonartech@sonarsource.com>
Thu, 30 Apr 2020 20:03:45 +0000 (20:03 +0000)
server/sonar-web/src/main/js/apps/background-tasks/components/TaskComponent.tsx
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/TaskComponent-test.tsx
server/sonar-web/src/main/js/apps/background-tasks/components/__tests__/__snapshots__/TaskComponent-test.tsx.snap
server/sonar-web/src/main/js/apps/overview/components/App.tsx
server/sonar-web/src/main/js/apps/projectsManagement/ProjectRow.tsx
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/ProjectRow-test.tsx
server/sonar-web/src/main/js/apps/projectsManagement/__tests__/__snapshots__/ProjectRow-test.tsx.snap

index bd9c27cb0583bcbb4f5074d241e54a4c284cd511..9a7b9662ebbebdad0dfd20ef4385bff47334692c 100644 (file)
@@ -23,7 +23,13 @@ import BranchIcon from 'sonar-ui-common/components/icons/BranchIcon';
 import PullRequestIcon from 'sonar-ui-common/components/icons/PullRequestIcon';
 import QualifierIcon from 'sonar-ui-common/components/icons/QualifierIcon';
 import Organization from '../../../components/shared/Organization';
-import { getBranchUrl, getProjectUrl, getPullRequestUrl } from '../../../helpers/urls';
+import {
+  getBranchUrl,
+  getPortfolioUrl,
+  getProjectUrl,
+  getPullRequestUrl
+} from '../../../helpers/urls';
+import { ComponentQualifier } from '../../../types/component';
 import TaskType from './TaskType';
 
 interface Props {
@@ -79,7 +85,9 @@ export default function TaskComponent({ task }: Props) {
 }
 
 function getTaskComponentUrl(componentKey: string, task: T.Task) {
-  if (task.branch) {
+  if (task.componentQualifier === ComponentQualifier.Portfolio) {
+    return getPortfolioUrl(componentKey);
+  } else if (task.branch) {
     return getBranchUrl(componentKey, task.branch);
   } else if (task.pullRequest) {
     return getPullRequestUrl(componentKey, task.pullRequest);
index 3b74956ba00690a522ed7fb5eca4ffcf5dc47821..13477c7d4dc86c9ff509a44ad6a65720a0739044 100644 (file)
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
+import { ComponentQualifier } from '../../../../types/component';
 import TaskComponent from '../TaskComponent';
 
-const TASK = {
-  componentKey: 'foo',
-  componentName: 'foo',
-  componentQualifier: 'TRK',
-  id: 'bar',
-  organization: 'org',
-  status: 'PENDING',
-  submittedAt: '2017-01-01',
-  submitterLogin: 'yoda',
-  type: 'REPORT'
-};
-
 it('renders correctly', () => {
-  expect(shallow(<TaskComponent task={TASK} />)).toMatchSnapshot();
-  expect(shallow(<TaskComponent task={{ ...TASK, componentKey: undefined }} />)).toMatchSnapshot();
+  expect(shallowRender()).toMatchSnapshot();
+  expect(shallowRender({ componentKey: undefined })).toMatchSnapshot('undefined key');
+  expect(shallowRender({ componentQualifier: ComponentQualifier.Portfolio })).toMatchSnapshot(
+    'portfolio'
+  );
+  expect(shallowRender({ branch: 'feature' })).toMatchSnapshot('branch');
+  expect(shallowRender({ branch: 'branch-6.7' })).toMatchSnapshot('branch');
+  expect(shallowRender({ pullRequest: 'pr-89' })).toMatchSnapshot('pull request');
 });
 
-it('renders correctly for branches and pullrequest', () => {
-  expect(shallow(<TaskComponent task={{ ...TASK, branch: 'feature' }} />)).toMatchSnapshot();
-  expect(shallow(<TaskComponent task={{ ...TASK, branch: 'branch-6.7' }} />)).toMatchSnapshot();
-  expect(shallow(<TaskComponent task={{ ...TASK, pullRequest: 'pr-89' }} />)).toMatchSnapshot();
-});
+function shallowRender(taskOverrides: Partial<T.Task> = {}) {
+  const TASK = {
+    componentKey: 'foo',
+    componentName: 'foo',
+    componentQualifier: 'TRK',
+    id: 'bar',
+    organization: 'org',
+    status: 'PENDING',
+    submittedAt: '2017-01-01',
+    submitterLogin: 'yoda',
+    type: 'REPORT'
+  };
+  return shallow(<TaskComponent task={{ ...TASK, ...taskOverrides }} />);
+}
index 653b69c156f299e9b2c8f7ad684f765c4001d7d3..c6704006123f0cf6ebb9045f6df1fe498c872f17 100644 (file)
@@ -34,20 +34,7 @@ exports[`renders correctly 1`] = `
 </td>
 `;
 
-exports[`renders correctly 2`] = `
-<td>
-  <span
-    className="note"
-  >
-    bar
-  </span>
-  <TaskType
-    type="REPORT"
-  />
-</td>
-`;
-
-exports[`renders correctly for branches and pullrequest 1`] = `
+exports[`renders correctly: branch 1`] = `
 <td>
   <BranchIcon
     className="little-spacer-right"
@@ -93,7 +80,7 @@ exports[`renders correctly for branches and pullrequest 1`] = `
 </td>
 `;
 
-exports[`renders correctly for branches and pullrequest 2`] = `
+exports[`renders correctly: branch 2`] = `
 <td>
   <BranchIcon
     className="little-spacer-right"
@@ -139,7 +126,40 @@ exports[`renders correctly for branches and pullrequest 2`] = `
 </td>
 `;
 
-exports[`renders correctly for branches and pullrequest 3`] = `
+exports[`renders correctly: portfolio 1`] = `
+<td>
+  <span
+    className="little-spacer-right"
+  >
+    <QualifierIcon
+      qualifier="VW"
+    />
+  </span>
+  <Connect(Organization)
+    organizationKey="org"
+  />
+  <Link
+    className="spacer-right"
+    onlyActiveOnIndex={false}
+    style={Object {}}
+    to={
+      Object {
+        "pathname": "/portfolio",
+        "query": Object {
+          "id": "foo",
+        },
+      }
+    }
+  >
+    foo
+  </Link>
+  <TaskType
+    type="REPORT"
+  />
+</td>
+`;
+
+exports[`renders correctly: pull request 1`] = `
 <td>
   <PullRequestIcon
     className="little-spacer-right"
@@ -183,3 +203,16 @@ exports[`renders correctly for branches and pullrequest 3`] = `
   />
 </td>
 `;
+
+exports[`renders correctly: undefined key 1`] = `
+<td>
+  <span
+    className="note"
+  >
+    bar
+  </span>
+  <TaskType
+    type="REPORT"
+  />
+</td>
+`;
index fbd6ea0c9503619993cdd5f01c78e8d94e1bae70..2fb35de7b063e6efd99e0ec83528cde31e99550b 100644 (file)
@@ -39,17 +39,6 @@ interface Props {
 }
 
 export class App extends React.PureComponent<Props> {
-  componentDidMount() {
-    const { component } = this.props;
-
-    if (this.isPortfolio()) {
-      this.props.router.replace({
-        pathname: '/portfolio',
-        query: { id: component.key }
-      });
-    }
-  }
-
   isPortfolio = () => {
     return ([ComponentQualifier.Portfolio, ComponentQualifier.SubPortfolio] as string[]).includes(
       this.props.component.qualifier
@@ -63,28 +52,24 @@ export class App extends React.PureComponent<Props> {
       return null;
     }
 
-    return (
+    return isPullRequest(branchLike) ? (
       <>
-        {isPullRequest(branchLike) ? (
-          <>
-            <Suggestions suggestions="pull_requests" />
-            <PullRequestOverview branchLike={branchLike} component={component} />
-          </>
-        ) : (
-          <>
-            <Suggestions suggestions="overview" />
+        <Suggestions suggestions="pull_requests" />
+        <PullRequestOverview branchLike={branchLike} component={component} />
+      </>
+    ) : (
+      <>
+        <Suggestions suggestions="overview" />
 
-            {!component.analysisDate ? (
-              <EmptyOverview
-                branchLike={branchLike}
-                branchLikes={branchLikes}
-                component={component}
-                hasAnalyses={this.props.isPending || this.props.isInProgress}
-              />
-            ) : (
-              <BranchOverview branchLike={branchLike} component={component} />
-            )}
-          </>
+        {!component.analysisDate ? (
+          <EmptyOverview
+            branchLike={branchLike}
+            branchLikes={branchLikes}
+            component={component}
+            hasAnalyses={this.props.isPending || this.props.isInProgress}
+          />
+        ) : (
+          <BranchOverview branchLike={branchLike} component={component} />
         )}
       </>
     );
index dc32383e3e1018d97c5b325f28296b630c054218..a12364c1a7f50dc76e7da03244c73cebe6a1675d 100644 (file)
@@ -25,6 +25,8 @@ import QualifierIcon from 'sonar-ui-common/components/icons/QualifierIcon';
 import DateTooltipFormatter from 'sonar-ui-common/components/intl/DateTooltipFormatter';
 import { Project } from '../../api/components';
 import PrivacyBadgeContainer from '../../components/common/PrivacyBadgeContainer';
+import { getPortfolioUrl, getProjectUrl } from '../../helpers/urls';
+import { ComponentQualifier } from '../../types/component';
 import './ProjectRow.css';
 import ProjectRowActions from './ProjectRowActions';
 
@@ -41,6 +43,12 @@ export default class ProjectRow extends React.PureComponent<Props> {
     this.props.onProjectCheck(this.props.project, checked);
   };
 
+  getComponentUrl(project: Project) {
+    return project.qualifier === ComponentQualifier.Portfolio
+      ? getPortfolioUrl(project.key)
+      : getProjectUrl(project.key);
+  }
+
   render() {
     const { organization, project, selected } = this.props;
 
@@ -51,10 +59,8 @@ export default class ProjectRow extends React.PureComponent<Props> {
         </td>
 
         <td className="nowrap hide-overflow project-row-text-cell">
-          <Link
-            className="link-with-icon"
-            to={{ pathname: '/dashboard', query: { id: project.key } }}>
-            <QualifierIcon qualifier={project.qualifier} />
+          <Link className="link-with-icon" to={this.getComponentUrl(project)}>
+            <QualifierIcon className="little-spacer-right" qualifier={project.qualifier} />
 
             <Tooltip overlay={project.name} placement="left">
               <span>{project.name}</span>
index 60deca5f98d9260dad7ef3b11557e78aaf9a2b0d..b1117bd40a633a82d1fba21dc0549eff09416eb1 100644 (file)
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
+import { ComponentQualifier, Visibility } from '../../../types/component';
 import ProjectRow from '../ProjectRow';
 
 const project = {
   key: 'project',
   name: 'Project',
-  qualifier: 'TRK',
-  visibility: 'private'
+  qualifier: ComponentQualifier.Project,
+  visibility: Visibility.Private
 };
 
 it('renders', () => {
   expect(shallowRender()).toMatchSnapshot();
   expect(
     shallowRender({ project: { ...project, lastAnalysisDate: '2017-04-08T00:00:00.000Z' } })
-  ).toMatchSnapshot();
+  ).toMatchSnapshot('with lastAnalysisDate');
+  expect(
+    shallowRender({ project: { ...project, qualifier: ComponentQualifier.Portfolio } })
+  ).toMatchSnapshot('portfolio');
 });
 
 it('checks project', () => {
index bae87bce87c8a899a4aa46db0a5461796ffd68f5..15c4f5964f2a03d16bb8e3d6a49224c80517eb58 100644 (file)
@@ -24,12 +24,14 @@ exports[`renders 1`] = `
         Object {
           "pathname": "/dashboard",
           "query": Object {
+            "branch": undefined,
             "id": "project",
           },
         }
       }
     >
       <QualifierIcon
+        className="little-spacer-right"
         qualifier="TRK"
       />
       <Tooltip
@@ -100,7 +102,108 @@ exports[`renders 1`] = `
 </tr>
 `;
 
-exports[`renders 2`] = `
+exports[`renders: portfolio 1`] = `
+<tr
+  data-project-key="project"
+>
+  <td
+    className="thin"
+  >
+    <Checkbox
+      checked={true}
+      onCheck={[Function]}
+      thirdState={false}
+    />
+  </td>
+  <td
+    className="nowrap hide-overflow project-row-text-cell"
+  >
+    <Link
+      className="link-with-icon"
+      onlyActiveOnIndex={false}
+      style={Object {}}
+      to={
+        Object {
+          "pathname": "/portfolio",
+          "query": Object {
+            "id": "project",
+          },
+        }
+      }
+    >
+      <QualifierIcon
+        className="little-spacer-right"
+        qualifier="VW"
+      />
+      <Tooltip
+        overlay="Project"
+        placement="left"
+      >
+        <span>
+          Project
+        </span>
+      </Tooltip>
+    </Link>
+  </td>
+  <td
+    className="thin nowrap"
+  >
+    <Connect(PrivacyBadge)
+      qualifier="VW"
+      tooltipProps={
+        Object {
+          "projectKey": "project",
+        }
+      }
+      visibility="private"
+    />
+  </td>
+  <td
+    className="nowrap hide-overflow project-row-text-cell"
+  >
+    <Tooltip
+      overlay="project"
+      placement="left"
+    >
+      <span
+        className="note"
+      >
+        project
+      </span>
+    </Tooltip>
+  </td>
+  <td
+    className="thin nowrap text-right"
+  >
+    <span
+      className="note"
+    >
+      —
+    </span>
+  </td>
+  <td
+    className="thin nowrap"
+  >
+    <ProjectRowActions
+      currentUser={
+        Object {
+          "login": "foo",
+        }
+      }
+      project={
+        Object {
+          "key": "project",
+          "name": "Project",
+          "qualifier": "VW",
+          "visibility": "private",
+        }
+      }
+    />
+  </td>
+</tr>
+`;
+
+exports[`renders: with lastAnalysisDate 1`] = `
 <tr
   data-project-key="project"
 >
@@ -124,12 +227,14 @@ exports[`renders 2`] = `
         Object {
           "pathname": "/dashboard",
           "query": Object {
+            "branch": undefined,
             "id": "project",
           },
         }
       }
     >
       <QualifierIcon
+        className="little-spacer-right"
         qualifier="TRK"
       />
       <Tooltip