]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7938 Display inheritance info for deprecated rules, and make related tests...
authorWouter Admiraal <wouter.admiraal@sonarsource.com>
Mon, 24 Dec 2018 14:09:38 +0000 (15:09 +0100)
committerSonarTech <sonartech@sonarsource.com>
Fri, 4 Jan 2019 19:21:02 +0000 (20:21 +0100)
13 files changed:
server/sonar-web/src/main/js/apps/quality-profiles/compare/__tests__/ComparisonForm-test.tsx
server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileActions-test.tsx
server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/ProfileContainer-test.tsx
server/sonar-web/src/main/js/apps/quality-profiles/components/__tests__/__snapshots__/ProfileActions-test.tsx.snap
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/ProfileRules-test.tsx
server/sonar-web/src/main/js/apps/quality-profiles/details/__tests__/__snapshots__/ProfileRules-test.tsx.snap
server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.tsx
server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/EvolutionDeprecated-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/EvolutionDeprecated-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/apps/quality-profiles/testUtils.ts [new file with mode: 0644]
server/sonar-web/src/main/js/apps/quality-profiles/utils.ts
server/sonar-web/src/main/js/helpers/testUtils.ts
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index f158d9d0bb5e47a4f33a0e19b3270be3607ed4f8..14f71216a8b51e2a155dbb5d3f52611f0904fc9d 100644 (file)
 import { shallow } from 'enzyme';
 import * as React from 'react';
 import ComparisonForm from '../ComparisonForm';
-import { createFakeProfile } from '../../utils';
+import { mockQualityProfile } from '../../testUtils';
 
 it('should render Select with right options', () => {
-  const profile = createFakeProfile();
+  const profile = mockQualityProfile();
   const profiles = [
     profile,
-    createFakeProfile({ key: 'another', name: 'another name' }),
-    createFakeProfile({ key: 'java', name: 'java', language: 'java' })
+    mockQualityProfile({ key: 'another', name: 'another name' }),
+    mockQualityProfile({ key: 'java', name: 'java', language: 'java' })
   ];
 
   const output = shallow(
index d2b6e3ff1ec8907f00890a42e8bfea4d411d1d72..e8f9318c739c1da335268e8a39a5b89c841a3140 100644 (file)
@@ -21,22 +21,16 @@ import * as React from 'react';
 import { shallow } from 'enzyme';
 import { ProfileActions } from '../ProfileActions';
 import { click, waitAndUpdate } from '../../../../helpers/testUtils';
+import { mockQualityProfile } from '../../testUtils';
 
-const PROFILE = {
+const PROFILE = mockQualityProfile({
   activeRuleCount: 68,
   activeDeprecatedRuleCount: 0,
-  childrenCount: 0,
   depth: 0,
-  isBuiltIn: false,
-  isDefault: false,
-  isInherited: false,
-  key: 'foo',
-  language: 'java',
-  languageName: 'Java',
-  name: 'Foo',
+  language: 'js',
   organization: 'org',
   rulesUpdatedAt: '2017-06-28T12:58:44+0000'
-};
+});
 
 it('renders with no permissions', () => {
   expect(
@@ -107,7 +101,7 @@ it('should copy profile', async () => {
 
   expect(push).toBeCalledWith({
     pathname: '/organizations/org/quality_profiles/show',
-    query: { language: 'java', name: 'new-name' }
+    query: { language: 'js', name: 'new-name' }
   });
   expect(wrapper.find('CopyProfileForm').exists()).toBe(false);
 });
index 0a10863e3deab5d6a5ba98b8987e1b240ab84ae4..41fad58dc0667ec17669b5e6c0e292eece6dac34 100644 (file)
@@ -24,13 +24,13 @@ import { WithRouterProps } from 'react-router';
 import ProfileContainer from '../ProfileContainer';
 import ProfileNotFound from '../ProfileNotFound';
 import ProfileHeader from '../../details/ProfileHeader';
-import { createFakeProfile } from '../../utils';
+import { mockQualityProfile } from '../../testUtils';
 
 const routerProps = { router: {} } as WithRouterProps;
 
 it('should render ProfileHeader', () => {
-  const targetProfile = createFakeProfile({ language: 'js', name: 'fake' });
-  const profiles = [targetProfile, createFakeProfile({ language: 'js', name: 'another' })];
+  const targetProfile = mockQualityProfile({ name: 'fake' });
+  const profiles = [targetProfile, mockQualityProfile({ name: 'another' })];
   const updateProfiles = jest.fn();
   const output = shallow(
     <ProfileContainer
@@ -49,10 +49,7 @@ it('should render ProfileHeader', () => {
 });
 
 it('should render ProfileNotFound', () => {
-  const profiles = [
-    createFakeProfile({ language: 'js', name: 'fake' }),
-    createFakeProfile({ language: 'js', name: 'another' })
-  ];
+  const profiles = [mockQualityProfile({ name: 'fake' }), mockQualityProfile({ name: 'another' })];
   const output = shallow(
     <ProfileContainer
       location={{ pathname: '', query: { language: 'js', name: 'random' } }}
@@ -67,11 +64,12 @@ it('should render ProfileNotFound', () => {
 });
 
 it('should render Helmet', () => {
-  const profiles = [createFakeProfile({ language: 'js', name: 'First Profile' })];
+  const name = 'First Profile';
+  const profiles = [mockQualityProfile({ name })];
   const updateProfiles = jest.fn();
   const output = shallow(
     <ProfileContainer
-      location={{ pathname: '', query: { language: 'js', name: 'First Profile' } }}
+      location={{ pathname: '', query: { language: 'js', name } }}
       organization={null}
       profiles={profiles}
       updateProfiles={updateProfiles}
@@ -81,5 +79,5 @@ it('should render Helmet', () => {
   );
   const helmet = output.find(Helmet);
   expect(helmet.length).toBe(1);
-  expect(helmet.prop('title')).toContain('First Profile');
+  expect(helmet.prop('title')).toContain(name);
 });
index 09322081e098cbc628c02aecde2473bc2c543f10..0578972611b75f9736a7bab6592129b6767f12a9 100644 (file)
@@ -10,7 +10,7 @@ exports[`renders with all permissions 1`] = `
           "pathname": "/organizations/org/rules",
           "query": Object {
             "activation": "false",
-            "qprofile": "foo",
+            "qprofile": "key",
           },
         }
       }
@@ -18,9 +18,9 @@ exports[`renders with all permissions 1`] = `
       quality_profiles.activate_more_rules
     </ActionsDropdownItem>
     <ActionsDropdownItem
-      download="foo.xml"
+      download="key.xml"
       id="quality-profile-backup"
-      to="/api/qualityprofiles/backup?profileKey=foo"
+      to="/api/qualityprofiles/backup?profileKey=key"
     >
       backup_verb
     </ActionsDropdownItem>
@@ -30,8 +30,8 @@ exports[`renders with all permissions 1`] = `
         Object {
           "pathname": "/organizations/org/quality_profiles/compare",
           "query": Object {
-            "language": "java",
-            "name": "Foo",
+            "language": "js",
+            "name": "name",
           },
         }
       }
@@ -72,9 +72,9 @@ exports[`renders with no permissions 1`] = `
 <Fragment>
   <ActionsDropdown>
     <ActionsDropdownItem
-      download="foo.xml"
+      download="key.xml"
       id="quality-profile-backup"
-      to="/api/qualityprofiles/backup?profileKey=foo"
+      to="/api/qualityprofiles/backup?profileKey=key"
     >
       backup_verb
     </ActionsDropdownItem>
@@ -84,8 +84,8 @@ exports[`renders with no permissions 1`] = `
         Object {
           "pathname": "/organizations/org/quality_profiles/compare",
           "query": Object {
-            "language": "java",
-            "name": "Foo",
+            "language": "js",
+            "name": "name",
           },
         }
       }
@@ -106,7 +106,7 @@ exports[`renders with permission to edit only 1`] = `
           "pathname": "/organizations/org/rules",
           "query": Object {
             "activation": "false",
-            "qprofile": "foo",
+            "qprofile": "key",
           },
         }
       }
@@ -114,9 +114,9 @@ exports[`renders with permission to edit only 1`] = `
       quality_profiles.activate_more_rules
     </ActionsDropdownItem>
     <ActionsDropdownItem
-      download="foo.xml"
+      download="key.xml"
       id="quality-profile-backup"
-      to="/api/qualityprofiles/backup?profileKey=foo"
+      to="/api/qualityprofiles/backup?profileKey=key"
     >
       backup_verb
     </ActionsDropdownItem>
@@ -126,8 +126,8 @@ exports[`renders with permission to edit only 1`] = `
         Object {
           "pathname": "/organizations/org/quality_profiles/compare",
           "query": Object {
-            "language": "java",
-            "name": "Foo",
+            "language": "js",
+            "name": "name",
           },
         }
       }
index 58804f381c45a12e4f010b8f4bab4e1d174cf249..a273f9a19a8d609a87b54962e95bd396f9c81cd1 100644 (file)
@@ -23,22 +23,16 @@ import ProfileRules from '../ProfileRules';
 import * as apiRules from '../../../../api/rules';
 import * as apiQP from '../../../../api/quality-profiles';
 import { waitAndUpdate } from '../../../../helpers/testUtils';
+import { mockQualityProfile } from '../../testUtils';
 
-const PROFILE = {
+const PROFILE = mockQualityProfile({
   activeRuleCount: 68,
   activeDeprecatedRuleCount: 0,
-  childrenCount: 0,
   depth: 0,
-  isBuiltIn: false,
-  isDefault: false,
-  isInherited: false,
-  key: 'foo',
-  language: 'java',
-  languageName: 'Java',
-  name: 'Foo',
+  language: 'js',
   organization: 'org',
   rulesUpdatedAt: '2017-06-28T12:58:44+0000'
-};
+});
 
 const EDITABLE_PROFILE = { ...PROFILE, actions: { edit: true } };
 
index 83bc3ede515d7733ba22ad56e869ef6cb8209211..9f3047b13218938bd1f04f41c258cdca438242d9 100644 (file)
@@ -29,14 +29,14 @@ exports[`should render the quality profiles rules with sonarway comparison 1`] =
         <ProfileRulesRowTotal
           count={68}
           organization="foo"
-          qprofile="foo"
+          qprofile="key"
           total={253}
         />
         <ProfileRulesRowOfType
           count={68}
           key="BUG"
           organization="foo"
-          qprofile="foo"
+          qprofile="key"
           total={68}
           type="BUG"
         />
@@ -44,7 +44,7 @@ exports[`should render the quality profiles rules with sonarway comparison 1`] =
           count={0}
           key="VULNERABILITY"
           organization="foo"
-          qprofile="foo"
+          qprofile="key"
           total={7}
           type="VULNERABILITY"
         />
@@ -52,7 +52,7 @@ exports[`should render the quality profiles rules with sonarway comparison 1`] =
           count={0}
           key="CODE_SMELL"
           organization="foo"
-          qprofile="foo"
+          qprofile="key"
           total={168}
           type="CODE_SMELL"
         />
@@ -60,7 +60,7 @@ exports[`should render the quality profiles rules with sonarway comparison 1`] =
           count={0}
           key="SECURITY_HOTSPOT"
           organization="foo"
-          qprofile="foo"
+          qprofile="key"
           total={10}
           type="SECURITY_HOTSPOT"
         />
@@ -68,9 +68,9 @@ exports[`should render the quality profiles rules with sonarway comparison 1`] =
     </table>
   </div>
   <ProfileRulesSonarWayComparison
-    language="java"
+    language="js"
     organization="foo"
-    profile="foo"
+    profile="key"
     sonarWayMissingRules={4}
     sonarway="sonarway"
   />
@@ -87,7 +87,7 @@ exports[`should show a button to activate more rules for admins 1`] = `
       "pathname": "/organizations/foo/rules",
       "query": Object {
         "activation": "false",
-        "qprofile": "foo",
+        "qprofile": "key",
       },
     }
   }
@@ -100,7 +100,7 @@ exports[`should show a deprecated rules warning message 1`] = `
 <ProfileRulesDeprecatedWarning
   activeDeprecatedRules={8}
   organization="foo"
-  profile="foo"
+  profile="key"
 />
 `;
 
index 85e946c340763b9ab7d3054997da760bc7461179..c8a11019cc9a57f11b75d3749867e9b82212c639 100644 (file)
@@ -30,55 +30,125 @@ interface Props {
   profiles: Profile[];
 }
 
-export default function EvolutionDeprecated(props: Props) {
-  const profilesWithDeprecations = props.profiles.filter(
-    profile => profile.activeDeprecatedRuleCount > 0
-  );
+interface InheritedRulesInfo {
+  count: number;
+  from: Profile;
+}
 
-  if (profilesWithDeprecations.length === 0) {
-    return null;
-  }
+export default class EvolutionDeprecated extends React.PureComponent<Props> {
+  getDeprecatedRulesInheritanceChain = (profile: Profile, profilesWithDeprecations: Profile[]) => {
+    let rules: InheritedRulesInfo[] = [];
+    let count = profile.activeDeprecatedRuleCount;
 
-  const sortedProfiles = sortBy(profilesWithDeprecations, p => -p.activeDeprecatedRuleCount);
+    if (count === 0) {
+      return rules;
+    }
 
-  return (
-    <div className="boxed-group boxed-group-inner quality-profiles-evolution-deprecated">
-      <div className="spacer-bottom">
-        <strong>{translate('quality_profiles.deprecated_rules')}</strong>
-      </div>
-      <div className="spacer-bottom">
-        {translateWithParameters(
-          'quality_profiles.deprecated_rules_are_still_activated',
-          profilesWithDeprecations.length
-        )}
-      </div>
-      <ul>
-        {sortedProfiles.map(profile => (
-          <li className="spacer-top" key={profile.key}>
-            <div className="text-ellipsis">
-              <ProfileLink
-                className="link-no-underline"
-                language={profile.language}
-                name={profile.name}
-                organization={props.organization}>
-                {profile.name}
-              </ProfileLink>
-            </div>
-            <div className="note">
-              {profile.languageName}
-              {', '}
-              <Link
-                className="text-muted"
-                to={getDeprecatedActiveRulesUrl({ qprofile: profile.key }, props.organization)}>
+    if (profile.parentKey) {
+      const parentProfile = profilesWithDeprecations.find(p => p.key === profile.parentKey);
+      if (parentProfile) {
+        const parentRules = this.getDeprecatedRulesInheritanceChain(
+          parentProfile,
+          profilesWithDeprecations
+        );
+        if (parentRules.length) {
+          count -= parentRules.reduce((n, rule) => n + rule.count, 0);
+          rules = rules.concat(parentRules);
+        }
+      }
+    }
+
+    if (count > 0) {
+      rules.push({
+        count,
+        from: profile
+      });
+    }
+
+    return rules;
+  };
+
+  renderInheritedInfo = (profile: Profile, profilesWithDeprecations: Profile[]) => {
+    const rules = this.getDeprecatedRulesInheritanceChain(profile, profilesWithDeprecations);
+    if (rules.length) {
+      return (
+        <>
+          {rules.map(rule => {
+            if (rule.from.key === profile.key) {
+              return null;
+            }
+
+            return (
+              <div className="muted" key={rule.from.key}>
+                {' '}
                 {translateWithParameters(
-                  'quality_profile.x_rules',
-                  profile.activeDeprecatedRuleCount
+                  'coding_rules.filters.inheritance.x_inherited_from_y',
+                  rule.count,
+                  rule.from.name
                 )}
-              </Link>
-            </div>
-          </li>
-        ))}
-      </ul>
-    </div>
-  );
+              </div>
+            );
+          })}
+        </>
+      );
+    }
+    return null;
+  };
+
+  render() {
+    const profilesWithDeprecations = this.props.profiles.filter(
+      profile => profile.activeDeprecatedRuleCount > 0
+    );
+
+    if (profilesWithDeprecations.length === 0) {
+      return null;
+    }
+
+    const sortedProfiles = sortBy(profilesWithDeprecations, p => -p.activeDeprecatedRuleCount);
+
+    return (
+      <div className="boxed-group boxed-group-inner quality-profiles-evolution-deprecated">
+        <div className="spacer-bottom">
+          <strong>{translate('quality_profiles.deprecated_rules')}</strong>
+        </div>
+        <div className="spacer-bottom">
+          {translateWithParameters(
+            'quality_profiles.deprecated_rules_are_still_activated',
+            profilesWithDeprecations.length
+          )}
+        </div>
+        <ul>
+          {sortedProfiles.map(profile => (
+            <li className="spacer-top" key={profile.key}>
+              <div className="text-ellipsis">
+                <ProfileLink
+                  className="link-no-underline"
+                  language={profile.language}
+                  name={profile.name}
+                  organization={this.props.organization}>
+                  {profile.name}
+                </ProfileLink>
+              </div>
+              <div className="note">
+                {profile.languageName}
+                {', '}
+                <Link
+                  className="text-muted"
+                  to={getDeprecatedActiveRulesUrl(
+                    { qprofile: profile.key },
+                    this.props.organization
+                  )}>
+                  {translateWithParameters(
+                    'quality_profile.x_rules',
+                    profile.activeDeprecatedRuleCount
+                  )}
+                </Link>
+                {this.renderInheritedInfo(profile, profilesWithDeprecations)}
+              </div>
+            </li>
+          ))}
+        </ul>
+      </div>
+    );
+  }
 }
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/EvolutionDeprecated-test.tsx b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/EvolutionDeprecated-test.tsx
new file mode 100644 (file)
index 0000000..8d3eb66
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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 EvolutionDeprecated from '../EvolutionDeprecated';
+import { mockQualityProfile } from '../../testUtils';
+
+it('should render correctly', () => {
+  const wrapper = shallow(
+    <EvolutionDeprecated
+      organization="foo"
+      profiles={[
+        mockQualityProfile({
+          key: 'qp-1',
+          name: 'Quality Profile 1',
+          activeDeprecatedRuleCount: 0
+        }),
+        mockQualityProfile({
+          key: 'qp-2',
+          name: 'Quality Profile 2',
+          childrenCount: 1,
+          activeDeprecatedRuleCount: 2
+        }),
+        mockQualityProfile({
+          key: 'qp-3',
+          name: 'Quality Profile 3',
+          depth: 2,
+          activeDeprecatedRuleCount: 2,
+          parentKey: 'qp-2'
+        }),
+        mockQualityProfile({
+          key: 'qp-4',
+          name: 'Quality Profile 4',
+          depth: 3,
+          activeDeprecatedRuleCount: 3,
+          parentKey: 'qp-3'
+        }),
+        mockQualityProfile({
+          key: 'qp-5',
+          name: 'Quality Profile 5',
+          depth: 4,
+          activeDeprecatedRuleCount: 4,
+          parentKey: 'qp-4'
+        })
+      ]}
+    />
+  );
+  expect(wrapper).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/EvolutionDeprecated-test.tsx.snap b/server/sonar-web/src/main/js/apps/quality-profiles/home/__tests__/__snapshots__/EvolutionDeprecated-test.tsx.snap
new file mode 100644 (file)
index 0000000..9c1ab50
--- /dev/null
@@ -0,0 +1,210 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+  className="boxed-group boxed-group-inner quality-profiles-evolution-deprecated"
+>
+  <div
+    className="spacer-bottom"
+  >
+    <strong>
+      quality_profiles.deprecated_rules
+    </strong>
+  </div>
+  <div
+    className="spacer-bottom"
+  >
+    quality_profiles.deprecated_rules_are_still_activated.4
+  </div>
+  <ul>
+    <li
+      className="spacer-top"
+      key="qp-5"
+    >
+      <div
+        className="text-ellipsis"
+      >
+        <ProfileLink
+          className="link-no-underline"
+          language="js"
+          name="Quality Profile 5"
+          organization="foo"
+        >
+          Quality Profile 5
+        </ProfileLink>
+      </div>
+      <div
+        className="note"
+      >
+        JavaScript
+        , 
+        <Link
+          className="text-muted"
+          onlyActiveOnIndex={false}
+          style={Object {}}
+          to={
+            Object {
+              "pathname": "/organizations/foo/rules",
+              "query": Object {
+                "activation": "true",
+                "qprofile": "qp-5",
+                "statuses": "DEPRECATED",
+              },
+            }
+          }
+        >
+          quality_profile.x_rules.4
+        </Link>
+        <div
+          className="muted"
+          key="qp-2"
+        >
+           
+          coding_rules.filters.inheritance.x_inherited_from_y.2.Quality Profile 2
+        </div>
+        <div
+          className="muted"
+          key="qp-4"
+        >
+           
+          coding_rules.filters.inheritance.x_inherited_from_y.1.Quality Profile 4
+        </div>
+      </div>
+    </li>
+    <li
+      className="spacer-top"
+      key="qp-4"
+    >
+      <div
+        className="text-ellipsis"
+      >
+        <ProfileLink
+          className="link-no-underline"
+          language="js"
+          name="Quality Profile 4"
+          organization="foo"
+        >
+          Quality Profile 4
+        </ProfileLink>
+      </div>
+      <div
+        className="note"
+      >
+        JavaScript
+        , 
+        <Link
+          className="text-muted"
+          onlyActiveOnIndex={false}
+          style={Object {}}
+          to={
+            Object {
+              "pathname": "/organizations/foo/rules",
+              "query": Object {
+                "activation": "true",
+                "qprofile": "qp-4",
+                "statuses": "DEPRECATED",
+              },
+            }
+          }
+        >
+          quality_profile.x_rules.3
+        </Link>
+        <div
+          className="muted"
+          key="qp-2"
+        >
+           
+          coding_rules.filters.inheritance.x_inherited_from_y.2.Quality Profile 2
+        </div>
+      </div>
+    </li>
+    <li
+      className="spacer-top"
+      key="qp-2"
+    >
+      <div
+        className="text-ellipsis"
+      >
+        <ProfileLink
+          className="link-no-underline"
+          language="js"
+          name="Quality Profile 2"
+          organization="foo"
+        >
+          Quality Profile 2
+        </ProfileLink>
+      </div>
+      <div
+        className="note"
+      >
+        JavaScript
+        , 
+        <Link
+          className="text-muted"
+          onlyActiveOnIndex={false}
+          style={Object {}}
+          to={
+            Object {
+              "pathname": "/organizations/foo/rules",
+              "query": Object {
+                "activation": "true",
+                "qprofile": "qp-2",
+                "statuses": "DEPRECATED",
+              },
+            }
+          }
+        >
+          quality_profile.x_rules.2
+        </Link>
+      </div>
+    </li>
+    <li
+      className="spacer-top"
+      key="qp-3"
+    >
+      <div
+        className="text-ellipsis"
+      >
+        <ProfileLink
+          className="link-no-underline"
+          language="js"
+          name="Quality Profile 3"
+          organization="foo"
+        >
+          Quality Profile 3
+        </ProfileLink>
+      </div>
+      <div
+        className="note"
+      >
+        JavaScript
+        , 
+        <Link
+          className="text-muted"
+          onlyActiveOnIndex={false}
+          style={Object {}}
+          to={
+            Object {
+              "pathname": "/organizations/foo/rules",
+              "query": Object {
+                "activation": "true",
+                "qprofile": "qp-3",
+                "statuses": "DEPRECATED",
+              },
+            }
+          }
+        >
+          quality_profile.x_rules.2
+        </Link>
+        <div
+          className="muted"
+          key="qp-2"
+        >
+           
+          coding_rules.filters.inheritance.x_inherited_from_y.2.Quality Profile 2
+        </div>
+      </div>
+    </li>
+  </ul>
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/testUtils.ts b/server/sonar-web/src/main/js/apps/quality-profiles/testUtils.ts
new file mode 100644 (file)
index 0000000..b737829
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * 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 { Profile } from './types';
+
+export function mockQualityProfile(overrides = {}): Profile {
+  return {
+    activeDeprecatedRuleCount: 2,
+    activeRuleCount: 10,
+    childrenCount: 0,
+    depth: 1,
+    isBuiltIn: false,
+    isDefault: false,
+    isInherited: false,
+    key: 'key',
+    language: 'js',
+    languageName: 'JavaScript',
+    name: 'name',
+    projectCount: 3,
+    organization: 'foo',
+    ...overrides
+  };
+}
index 0444984f37940816200164db2e269a154d2844a1..5208ed94781ebcf3f2e49aa9f476ee0002359aa1 100644 (file)
@@ -51,21 +51,6 @@ export function sortProfiles(profiles: BaseProfile[]): Profile[] {
   return result;
 }
 
-export function createFakeProfile(overrides?: any) {
-  return {
-    key: 'key',
-    name: 'name',
-    isDefault: false,
-    isInherited: false,
-    language: 'js',
-    languageName: 'JavaScript',
-    activeRuleCount: 10,
-    activeDeprecatedRuleCount: 2,
-    projectCount: 3,
-    ...overrides
-  };
-}
-
 export function isStagnant(profile: Profile): boolean {
   if (profile.rulesUpdatedAt) {
     const updateDate = parseDate(profile.rulesUpdatedAt);
index 668e8ba170e42dd95d2434aafa6a5ceba1135bb6..c2cac596d28c3dc5a534fbaecd49e92d72fc91ba 100644 (file)
@@ -152,7 +152,14 @@ export function mockComponent(overrides = {}): T.Component {
     organization: 'foo',
     qualifier: 'TRK',
     qualityGate: { isDefault: true, key: '30', name: 'Sonar way' },
-    qualityProfiles: [mockQualityProfile()],
+    qualityProfiles: [
+      {
+        deleted: false,
+        key: 'my-qp',
+        language: 'ts',
+        name: 'Sonar way'
+      }
+    ],
     tags: [],
     ...overrides
   };
@@ -172,13 +179,3 @@ export function mockOrganization(overrides = {}): T.Organization {
     ...overrides
   };
 }
-
-export function mockQualityProfile(overrides = {}): T.ComponentQualityProfile {
-  return {
-    deleted: false,
-    key: 'my-qp',
-    language: 'ts',
-    name: 'Sonar way',
-    ...overrides
-  };
-}
index 180db4334de75240e4f1e049807e0684a154486c..469709822628c8b487a94b42d7c1f25c1da52767 100644 (file)
@@ -1361,6 +1361,7 @@ coding_rules.filters.inheritance=Inheritance
 coding_rules.filters.inheritance.inactive=Inheritance criterion is available when an inherited quality profile is selected
 coding_rules.filters.inheritance.none=Not Inherited
 coding_rules.filters.inheritance.inherited=Inherited
+coding_rules.filters.inheritance.x_inherited_from_y={0} inherited from "{1}"
 coding_rules.filters.inheritance.overrides=Overridden
 coding_rules.filters.key=Key
 coding_rules.filters.language=Language