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(
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(
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);
});
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
});
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' } }}
});
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}
);
const helmet = output.find(Helmet);
expect(helmet.length).toBe(1);
- expect(helmet.prop('title')).toContain('First Profile');
+ expect(helmet.prop('title')).toContain(name);
});
"pathname": "/organizations/org/rules",
"query": Object {
"activation": "false",
- "qprofile": "foo",
+ "qprofile": "key",
},
}
}
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>
Object {
"pathname": "/organizations/org/quality_profiles/compare",
"query": Object {
- "language": "java",
- "name": "Foo",
+ "language": "js",
+ "name": "name",
},
}
}
<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>
Object {
"pathname": "/organizations/org/quality_profiles/compare",
"query": Object {
- "language": "java",
- "name": "Foo",
+ "language": "js",
+ "name": "name",
},
}
}
"pathname": "/organizations/org/rules",
"query": Object {
"activation": "false",
- "qprofile": "foo",
+ "qprofile": "key",
},
}
}
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>
Object {
"pathname": "/organizations/org/quality_profiles/compare",
"query": Object {
- "language": "java",
- "name": "Foo",
+ "language": "js",
+ "name": "name",
},
}
}
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 } };
<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"
/>
count={0}
key="VULNERABILITY"
organization="foo"
- qprofile="foo"
+ qprofile="key"
total={7}
type="VULNERABILITY"
/>
count={0}
key="CODE_SMELL"
organization="foo"
- qprofile="foo"
+ qprofile="key"
total={168}
type="CODE_SMELL"
/>
count={0}
key="SECURITY_HOTSPOT"
organization="foo"
- qprofile="foo"
+ qprofile="key"
total={10}
type="SECURITY_HOTSPOT"
/>
</table>
</div>
<ProfileRulesSonarWayComparison
- language="java"
+ language="js"
organization="foo"
- profile="foo"
+ profile="key"
sonarWayMissingRules={4}
sonarway="sonarway"
/>
"pathname": "/organizations/foo/rules",
"query": Object {
"activation": "false",
- "qprofile": "foo",
+ "qprofile": "key",
},
}
}
<ProfileRulesDeprecatedWarning
activeDeprecatedRules={8}
organization="foo"
- profile="foo"
+ profile="key"
/>
`;
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>
+ );
+ }
}
--- /dev/null
+/*
+ * 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();
+});
--- /dev/null
+// 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>
+`;
--- /dev/null
+/*
+ * 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
+ };
+}
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);
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
};
...overrides
};
}
-
-export function mockQualityProfile(overrides = {}): T.ComponentQualityProfile {
- return {
- deleted: false,
- key: 'my-qp',
- language: 'ts',
- name: 'Sonar way',
- ...overrides
- };
-}
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