]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-11015 Allow to add links for SonarQube global spaces (#531)
authorStas Vilchik <stas.vilchik@sonarsource.com>
Thu, 19 Jul 2018 13:04:53 +0000 (15:04 +0200)
committerSonarTech <sonartech@sonarsource.com>
Wed, 25 Jul 2018 18:21:20 +0000 (20:21 +0200)
server/sonar-docs/src/pages/analysis/index.md
server/sonar-docs/src/pages/quality-profiles.md
server/sonar-web/src/main/js/components/docs/DocLink.tsx
server/sonar-web/src/main/js/components/docs/__tests__/DocLink-test.tsx
server/sonar-web/src/main/js/components/docs/__tests__/__snapshots__/DocLink-test.tsx.snap

index 8ddcdcd12d97e43bd3ef841d52da589e79d556fb..066993ae8878f94c86ab5d60084e24d56df4e8e8 100644 (file)
@@ -29,7 +29,7 @@ SonarQube can perform analysis on 20+ different languages. The outcome of this a
 * A dynamic analysis of code can be performed on certain languages.
 
 ## Will _all_ files be analyzed?
-By default, only files that are recognized by a language analyzer are loaded into the project during analysis. For example if your SonarQube instance had only SonarJava SonarJS on board, all .java and .js files would be loaded, but .xml files would be ignored. However, it is possible to import all text files in a project by setting **Settings > Exclusions > Files > Import unknown files** to true. 
+By default, only files that are recognized by a language analyzer are loaded into the project during analysis. For example if your SonarQube instance had only SonarJava SonarJS on board, all .java and .js files would be loaded, but .xml files would be ignored. However, it is possible to import all text files in a project by setting [**Settings > Exclusions > Files > Import unknown files**](/#sonarqube-admin#/admin/settings?category=exclusions) to true. 
 
 ## What happens during analysis?
 During analysis, data is requested from the server, the files provided to the analysis are analyzed, and the resulting data is sent back to the server at the end in the form of a report, which is then analyzed asynchronously server-side.
index c6c746dbe09429516037194e7cf79151011e2cac..1e9c03e95e6026e68459733b552c221e8d0ecbb8 100644 (file)
@@ -12,7 +12,7 @@ Ideally, all projects will be measured with the same profile for any given langu
 * You want to ensure stronger requirements on some of your applications (internal frameworks for example).
 * Etc.
 
-Which is why you can define as many quality profiles as you wish even though it is recommended to have as few Quality Profiles as possible to ensure consistency across the projects in your company. To manage quality profiles, go to **Quality Profiles** (top bar), where you'll find profiles grouped by language.
+Which is why you can define as many quality profiles as you wish even though it is recommended to have as few Quality Profiles as possible to ensure consistency across the projects in your company. To manage quality profiles, go to <!-- sonarqube -->[**Quality Profiles**](/#sonarqube#/profiles)<!-- /sonarqube --><!-- sonarcloud -->**Quality Profiles** page of your organization<!-- /sonarcloud -->, where you'll find profiles grouped by language.
 
 Each language plugin comes with a predefined, built-in profile (usually called "Sonar way") so that you can get started very quickly with SonarQube analyses. This is why as soon as you install a new language plugin, at least one quality profile will be available for you. Each language must have a default profile (marked with the Default tag). Projects that are not explicitly associated with a specific profile will be analyzed using the language's default profile.
 
index 5a0eb5de4b8e66cb7d0c188233b41e45bd9257fd..f091b3422942c10cf5753e2f01ff64f918914748 100644 (file)
@@ -20,6 +20,7 @@
 import * as React from 'react';
 import { Link } from 'react-router';
 import DetachIcon from '../icons-components/DetachIcon';
+import { isSonarCloud } from '../../helpers/system';
 
 interface OwnProps {
   customProps?: {
@@ -30,8 +31,14 @@ interface OwnProps {
 type Props = OwnProps & React.AnchorHTMLAttributes<HTMLAnchorElement>;
 
 const SONARCLOUD_LINK = '/#sonarcloud#/';
+const SONARQUBE_LINK = '/#sonarqube#/';
+const SONARQUBE_ADMIN_LINK = '/#sonarqube-admin#/';
 
 export default class DocLink extends React.PureComponent<Props> {
+  static contextTypes = {
+    canAdmin: () => null
+  };
+
   handleClickOnAnchor = (event: React.MouseEvent<HTMLAnchorElement>) => {
     const { customProps, href = '#' } = this.props;
     if (customProps && customProps.onAnchorClick) {
@@ -50,15 +57,24 @@ export default class DocLink extends React.PureComponent<Props> {
     }
 
     if (href && href.startsWith('/')) {
-      let url = `/documentation/${href.substr(1)}`;
       if (href.startsWith(SONARCLOUD_LINK)) {
-        url = `/${href.substr(SONARCLOUD_LINK.length)}`;
+        return <SonarCloudLink url={href}>{children}</SonarCloudLink>;
+      } else if (href.startsWith(SONARQUBE_LINK)) {
+        return <SonarQubeLink url={href}>{children}</SonarQubeLink>;
+      } else if (href.startsWith(SONARQUBE_ADMIN_LINK)) {
+        return (
+          <SonarQubeAdminLink canAdmin={this.context.canAdmin} url={href}>
+            {children}
+          </SonarQubeAdminLink>
+        );
+      } else {
+        const url = '/documentation' + href;
+        return (
+          <Link to={url} {...other}>
+            {children}
+          </Link>
+        );
       }
-      return (
-        <Link to={url} {...other}>
-          {children}
-        </Link>
-      );
     }
 
     return (
@@ -74,3 +90,54 @@ export default class DocLink extends React.PureComponent<Props> {
     );
   }
 }
+
+interface SonarCloudLinkProps {
+  children: React.ReactNode;
+  url: string;
+}
+
+function SonarCloudLink({ children, url }: SonarCloudLinkProps) {
+  if (!isSonarCloud()) {
+    return <>{children}</>;
+  } else {
+    const to = `/${url.substr(SONARCLOUD_LINK.length)}`;
+    return <Link to={to}>{children}</Link>;
+  }
+}
+
+interface SonarQubeLinkProps {
+  children: React.ReactNode;
+  url: string;
+}
+
+function SonarQubeLink({ children, url }: SonarQubeLinkProps) {
+  if (isSonarCloud()) {
+    return <>{children}</>;
+  } else {
+    const to = `/${url.substr(SONARQUBE_LINK.length)}`;
+    return (
+      <Link target="_blank" to={to}>
+        {children}
+      </Link>
+    );
+  }
+}
+
+interface SonarQubeAdminLinkProps {
+  canAdmin: boolean;
+  children: React.ReactNode;
+  url: string;
+}
+
+function SonarQubeAdminLink({ canAdmin, children, url }: SonarQubeAdminLinkProps) {
+  if (isSonarCloud() || !canAdmin) {
+    return <>{children}</>;
+  } else {
+    const to = `/${url.substr(SONARQUBE_ADMIN_LINK.length)}`;
+    return (
+      <Link target="_blank" to={to}>
+        {children}
+      </Link>
+    );
+  }
+}
index 4b394343c351812773e6e9750811a55691aed947..54871ff53f366c886f305e3f7036f52f294de4e0 100644 (file)
 import * as React from 'react';
 import { shallow } from 'enzyme';
 import DocLink from '../DocLink';
+import { isSonarCloud } from '../../../helpers/system';
+
+jest.mock('../../../helpers/system', () => ({
+  isSonarCloud: jest.fn(() => false)
+}));
 
 it('should render simple link', () => {
-  expect(shallow(<DocLink href="http://sample.com" />)).toMatchSnapshot();
+  expect(shallow(<DocLink href="http://sample.com">link text</DocLink>)).toMatchSnapshot();
 });
 
 it('should render documentation link', () => {
-  expect(shallow(<DocLink href="/foo/bar" />)).toMatchSnapshot();
+  expect(shallow(<DocLink href="/foo/bar">link text</DocLink>)).toMatchSnapshot();
+});
+
+it('should render sonarcloud link on sonarcloud', () => {
+  (isSonarCloud as jest.Mock).mockImplementationOnce(() => true);
+  const wrapper = shallow(<DocLink href="/#sonarcloud#/foo/bar">link text</DocLink>);
+  expect(wrapper).toMatchSnapshot();
+  expect(wrapper.find('SonarCloudLink').dive()).toMatchSnapshot();
+});
+
+it('should not render sonarcloud link on sonarcloud', () => {
+  (isSonarCloud as jest.Mock).mockImplementationOnce(() => false);
+  const wrapper = shallow(<DocLink href="/#sonarcloud#/foo/bar">link text</DocLink>);
+  expect(wrapper.find('SonarCloudLink').dive()).toMatchSnapshot();
+});
+
+it('should render sonarqube link on sonarqube', () => {
+  const wrapper = shallow(<DocLink href="/#sonarqube#/foo/bar">link text</DocLink>);
+  expect(wrapper).toMatchSnapshot();
+  expect(wrapper.find('SonarQubeLink').dive()).toMatchSnapshot();
+});
+
+it('should not render sonarqube link on sonarcloud', () => {
+  (isSonarCloud as jest.Mock).mockImplementationOnce(() => true);
+  const wrapper = shallow(<DocLink href="/#sonarqube#/foo/bar">link text</DocLink>);
+  expect(wrapper.find('SonarQubeLink').dive()).toMatchSnapshot();
+});
+
+it('should render sonarqube admin link on sonarqube for admin', () => {
+  const wrapper = shallow(<DocLink href="/#sonarqube-admin#/foo/bar">link text</DocLink>, {
+    context: { canAdmin: true }
+  });
+  expect(wrapper).toMatchSnapshot();
+  expect(wrapper.find('SonarQubeAdminLink').dive()).toMatchSnapshot();
+});
+
+it('should not render sonarqube admin link on sonarqube for non-admin', () => {
+  const wrapper = shallow(<DocLink href="/#sonarqube-admin#/foo/bar">link text</DocLink>);
+  expect(wrapper.find('SonarQubeAdminLink').dive()).toMatchSnapshot();
 });
 
-it('should render sonarcloud link', () => {
-  expect(shallow(<DocLink href="/#sonarcloud#/foo/bar" />)).toMatchSnapshot();
+it('should not render sonarqube admin link on sonarcloud', () => {
+  (isSonarCloud as jest.Mock).mockImplementationOnce(() => true);
+  const wrapper = shallow(<DocLink href="/#sonarqube-admin#/foo/bar">link text</DocLink>, {
+    context: { canAdmin: true }
+  });
+  expect(wrapper.find('SonarQubeAdminLink').dive()).toMatchSnapshot();
 });
 
 it.skip('should render documentation anchor', () => {
-  expect(shallow(<DocLink href="#quality-profiles" />)).toMatchSnapshot();
+  expect(shallow(<DocLink href="#quality-profiles">link text</DocLink>)).toMatchSnapshot();
 });
index 4d443347a2e7bf295c26ed0f44cbd6bf431ee1f5..04cabb5505589e40068c7cb86ef5b434f73001cd 100644 (file)
@@ -1,11 +1,37 @@
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
+exports[`should not render sonarcloud link on sonarcloud 1`] = `
+<React.Fragment>
+  link text
+</React.Fragment>
+`;
+
+exports[`should not render sonarqube admin link on sonarcloud 1`] = `
+<React.Fragment>
+  link text
+</React.Fragment>
+`;
+
+exports[`should not render sonarqube admin link on sonarqube for non-admin 1`] = `
+<React.Fragment>
+  link text
+</React.Fragment>
+`;
+
+exports[`should not render sonarqube link on sonarcloud 1`] = `
+<React.Fragment>
+  link text
+</React.Fragment>
+`;
+
 exports[`should render documentation link 1`] = `
 <Link
   onlyActiveOnIndex={false}
   style={Object {}}
   to="/documentation/foo/bar"
-/>
+>
+  link text
+</Link>
 `;
 
 exports[`should render simple link 1`] = `
@@ -14,7 +40,9 @@ exports[`should render simple link 1`] = `
     href="http://sample.com"
     rel="noopener noreferrer"
     target="_blank"
-  />
+  >
+    link text
+  </a>
   <DetachIcon
     className="text-muted little-spacer-left little-spacer-right vertical-baseline"
     size={12}
@@ -22,10 +50,59 @@ exports[`should render simple link 1`] = `
 </React.Fragment>
 `;
 
-exports[`should render sonarcloud link 1`] = `
+exports[`should render sonarcloud link on sonarcloud 1`] = `
+<SonarCloudLink
+  url="/#sonarcloud#/foo/bar"
+>
+  link text
+</SonarCloudLink>
+`;
+
+exports[`should render sonarcloud link on sonarcloud 2`] = `
+<Link
+  onlyActiveOnIndex={false}
+  style={Object {}}
+  to="/foo/bar"
+>
+  link text
+</Link>
+`;
+
+exports[`should render sonarqube admin link on sonarqube for admin 1`] = `
+<SonarQubeAdminLink
+  canAdmin={true}
+  url="/#sonarqube-admin#/foo/bar"
+>
+  link text
+</SonarQubeAdminLink>
+`;
+
+exports[`should render sonarqube admin link on sonarqube for admin 2`] = `
+<Link
+  onlyActiveOnIndex={false}
+  style={Object {}}
+  target="_blank"
+  to="/foo/bar"
+>
+  link text
+</Link>
+`;
+
+exports[`should render sonarqube link on sonarqube 1`] = `
+<SonarQubeLink
+  url="/#sonarqube#/foo/bar"
+>
+  link text
+</SonarQubeLink>
+`;
+
+exports[`should render sonarqube link on sonarqube 2`] = `
 <Link
   onlyActiveOnIndex={false}
   style={Object {}}
+  target="_blank"
   to="/foo/bar"
-/>
+>
+  link text
+</Link>
 `;