aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/coding-rules/components
diff options
context:
space:
mode:
Diffstat (limited to 'server/sonar-web/src/main/js/apps/coding-rules/components')
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx7
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/BulkChange.tsx55
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/BulkChangeModal.tsx41
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/Facet.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetails.tsx32
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsCustomRules.tsx20
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsProfiles.tsx47
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/RuleListItem.tsx61
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/BulkChange-test.tsx64
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetails-test.tsx98
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/BulkChange-test.tsx.snap99
-rw-r--r--server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetails-test.tsx.snap204
12 files changed, 584 insertions, 149 deletions
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx
index d5264e69c5d..ac5b6325bb9 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/ActivationFormModal.tsx
@@ -160,10 +160,9 @@ export default class ActivationFormModal extends React.PureComponent<Props, Stat
</div>
<div className="modal-body">
- {!isUpdateMode &&
- activeInAllProfiles && (
- <Alert variant="info">{translate('coding_rules.active_in_all_profiles')}</Alert>
- )}
+ {!isUpdateMode && activeInAllProfiles && (
+ <Alert variant="info">{translate('coding_rules.active_in_all_profiles')}</Alert>
+ )}
<div className="modal-field">
<label>{translate('coding_rules.quality_profile')}</label>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/BulkChange.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/BulkChange.tsx
index 508c9bfc621..4a7fe801eb5 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/BulkChange.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/BulkChange.tsx
@@ -101,44 +101,41 @@ export default class BulkChange extends React.PureComponent<Props, State> {
{translate('coding_rules.activate_in')}…
</a>
</li>
- {allowActivateOnProfile &&
- profile && (
- <li>
- <a href="#" onClick={this.handleActivateInProfileClick}>
- {translate('coding_rules.activate_in')} <strong>{profile.name}</strong>
- </a>
- </li>
- )}
+ {allowActivateOnProfile && profile && (
+ <li>
+ <a href="#" onClick={this.handleActivateInProfileClick}>
+ {translate('coding_rules.activate_in')} <strong>{profile.name}</strong>
+ </a>
+ </li>
+ )}
<li>
<a href="#" onClick={this.handleDeactivateClick}>
{translate('coding_rules.deactivate_in')}…
</a>
</li>
- {allowDeactivateOnProfile &&
- profile && (
- <li>
- <a href="#" onClick={this.handleDeactivateInProfileClick}>
- {translate('coding_rules.deactivate_in')} <strong>{profile.name}</strong>
- </a>
- </li>
- )}
+ {allowDeactivateOnProfile && profile && (
+ <li>
+ <a href="#" onClick={this.handleDeactivateInProfileClick}>
+ {translate('coding_rules.deactivate_in')} <strong>{profile.name}</strong>
+ </a>
+ </li>
+ )}
</ul>
}>
<Button className="js-bulk-change">{translate('bulk_change')}</Button>
</Dropdown>
- {this.state.modal &&
- this.state.action && (
- <BulkChangeModal
- action={this.state.action}
- languages={this.props.languages}
- onClose={this.closeModal}
- organization={this.props.organization}
- profile={this.state.profile}
- query={this.props.query}
- referencedProfiles={this.props.referencedProfiles}
- total={this.props.total}
- />
- )}
+ {this.state.modal && this.state.action && (
+ <BulkChangeModal
+ action={this.state.action}
+ languages={this.props.languages}
+ onClose={this.closeModal}
+ organization={this.props.organization}
+ profile={this.state.profile}
+ query={this.props.query}
+ referencedProfiles={this.props.referencedProfiles}
+ total={this.props.total}
+ />
+ )}
</>
);
}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/BulkChangeModal.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/BulkChangeModal.tsx
index a7df07d3130..922c6bb563c 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/BulkChangeModal.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/BulkChangeModal.tsx
@@ -207,27 +207,26 @@ export default class BulkChangeModal extends React.PureComponent<Props, State> {
<div className="modal-body">
{this.state.results.map(this.renderResult)}
- {!this.state.finished &&
- !this.state.submitting && (
- <div className="modal-field">
- <h3>
- <label htmlFor="coding-rules-bulk-change-profile">
- {action === 'activate'
- ? translate('coding_rules.activate_in')
- : translate('coding_rules.deactivate_in')}
- </label>
- </h3>
- {profile ? (
- <span>
- {profile.name}
- {' — '}
- {translate('are_you_sure')}
- </span>
- ) : (
- this.renderProfileSelect()
- )}
- </div>
- )}
+ {!this.state.finished && !this.state.submitting && (
+ <div className="modal-field">
+ <h3>
+ <label htmlFor="coding-rules-bulk-change-profile">
+ {action === 'activate'
+ ? translate('coding_rules.activate_in')
+ : translate('coding_rules.deactivate_in')}
+ </label>
+ </h3>
+ {profile ? (
+ <span>
+ {profile.name}
+ {' — '}
+ {translate('are_you_sure')}
+ </span>
+ ) : (
+ this.renderProfileSelect()
+ )}
+ </div>
+ )}
</div>
<footer className="modal-foot">
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/Facet.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/Facet.tsx
index a39e77d2398..2a766236a32 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/Facet.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/Facet.tsx
@@ -118,8 +118,9 @@ export default class Facet extends React.PureComponent<Props> {
{this.props.children}
</FacetHeader>
- {this.props.open &&
- items !== undefined && <FacetItemsList>{items.map(this.renderItem)}</FacetItemsList>}
+ {this.props.open && items !== undefined && (
+ <FacetItemsList>{items.map(this.renderItem)}</FacetItemsList>
+ )}
{this.props.open && this.props.renderFooter !== undefined && this.props.renderFooter()}
</FacetBox>
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetails.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetails.tsx
index b56478416fe..a2a66f8050a 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetails.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetails.tsx
@@ -103,8 +103,8 @@ export default class RuleDetails extends React.PureComponent<Props, State> {
handleTagsChange = (tags: string[]) => {
// optimistic update
const oldTags = this.state.ruleDetails && this.state.ruleDetails.tags;
- this.setState(
- state => (state.ruleDetails ? { ruleDetails: { ...state.ruleDetails, tags } } : null)
+ this.setState(state =>
+ state.ruleDetails ? { ruleDetails: { ...state.ruleDetails, tags } } : null
);
updateRule({
key: this.props.ruleKey,
@@ -112,9 +112,8 @@ export default class RuleDetails extends React.PureComponent<Props, State> {
tags: tags.join()
}).catch(() => {
if (this.mounted) {
- this.setState(
- state =>
- state.ruleDetails ? { ruleDetails: { ...state.ruleDetails, tags: oldTags } } : null
+ this.setState(state =>
+ state.ruleDetails ? { ruleDetails: { ...state.ruleDetails, tags: oldTags } } : null
);
}
});
@@ -239,18 +238,17 @@ export default class RuleDetails extends React.PureComponent<Props, State> {
/>
)}
- {!ruleDetails.isTemplate &&
- !hideQualityProfiles && (
- <RuleDetailsProfiles
- activations={this.state.actives}
- canWrite={canWrite}
- onActivate={this.handleActivate}
- onDeactivate={this.handleDeactivate}
- organization={organization}
- referencedProfiles={referencedProfiles}
- ruleDetails={ruleDetails}
- />
- )}
+ {!ruleDetails.isTemplate && !hideQualityProfiles && (
+ <RuleDetailsProfiles
+ activations={this.state.actives}
+ canWrite={canWrite}
+ onActivate={this.handleActivate}
+ onDeactivate={this.handleDeactivate}
+ organization={organization}
+ referencedProfiles={referencedProfiles}
+ ruleDetails={ruleDetails}
+ />
+ )}
{!ruleDetails.isTemplate && (
<RuleDetailsIssues organization={organization} ruleDetails={ruleDetails} />
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsCustomRules.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsCustomRules.tsx
index 966a96ca51c..c0aceb9a116 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsCustomRules.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsCustomRules.tsx
@@ -109,15 +109,17 @@ export default class RuleDetailsCustomRules extends React.PureComponent<Props, S
<td className="coding-rules-detail-list-parameters">
{rule.params &&
- rule.params.filter(param => param.defaultValue).map(param => (
- <div className="coding-rules-detail-list-parameter" key={param.key}>
- <span className="key">{param.key}</span>
- <span className="sep">:&nbsp;</span>
- <span className="value" title={param.defaultValue}>
- {param.defaultValue}
- </span>
- </div>
- ))}
+ rule.params
+ .filter(param => param.defaultValue)
+ .map(param => (
+ <div className="coding-rules-detail-list-parameter" key={param.key}>
+ <span className="key">{param.key}</span>
+ <span className="sep">:&nbsp;</span>
+ <span className="value" title={param.defaultValue}>
+ {param.defaultValue}
+ </span>
+ </div>
+ ))}
</td>
{this.props.canChange && (
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsProfiles.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsProfiles.tsx
index f112a6edd95..58dae28f955 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsProfiles.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleDetailsProfiles.tsx
@@ -42,30 +42,7 @@ interface Props {
ruleDetails: T.RuleDetails;
}
-interface State {
- loading: boolean;
-}
-
-export default class RuleDetailsProfiles extends React.PureComponent<Props, State> {
- mounted = false;
-
- componentDidMount() {
- this.mounted = true;
- this.fetchProfiles();
- }
-
- componentDidUpdate(prevProps: Props) {
- if (prevProps.ruleDetails.key !== this.props.ruleDetails.key) {
- this.fetchProfiles();
- }
- }
-
- componentWillUnmount() {
- this.mounted = false;
- }
-
- fetchProfiles = () => this.setState({ loading: true });
-
+export default class RuleDetailsProfiles extends React.PureComponent<Props> {
handleActivate = () => this.props.onActivate();
handleDeactivate = (key?: string) => {
@@ -119,12 +96,11 @@ export default class RuleDetailsProfiles extends React.PureComponent<Props, Stat
<SeverityHelper className="display-inline-flex-center" severity={activation.severity} />
</span>
</Tooltip>
- {parentActivation !== undefined &&
- activation.severity !== parentActivation.severity && (
- <div className="coding-rules-detail-quality-profile-inheritance">
- {translate('coding_rules.original')} {translate('severity', parentActivation.severity)}
- </div>
- )}
+ {parentActivation !== undefined && activation.severity !== parentActivation.severity && (
+ <div className="coding-rules-detail-quality-profile-inheritance">
+ {translate('coding_rules.original')} {translate('severity', parentActivation.severity)}
+ </div>
+ )}
</td>
);
@@ -143,12 +119,11 @@ export default class RuleDetailsProfiles extends React.PureComponent<Props, Stat
<span className="value" title={param.value}>
{param.value}
</span>
- {parentActivation &&
- param.value !== originalValue && (
- <div className="coding-rules-detail-quality-profile-inheritance">
- {translate('coding_rules.original')} <span className="value">{originalValue}</span>
- </div>
- )}
+ {parentActivation && param.value !== originalValue && (
+ <div className="coding-rules-detail-quality-profile-inheritance">
+ {translate('coding_rules.original')} <span className="value">{originalValue}</span>
+ </div>
+ )}
</div>
);
};
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleListItem.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleListItem.tsx
index b48cbaa6d9c..faa9b262d8b 100644
--- a/server/sonar-web/src/main/js/apps/coding-rules/components/RuleListItem.tsx
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/RuleListItem.tsx
@@ -89,37 +89,36 @@ export default class RuleListItem extends React.PureComponent<Props> {
return (
<td className="coding-rule-table-meta-cell coding-rule-activation">
<SeverityIcon severity={activation.severity} />
- {selectedProfile &&
- selectedProfile.parentName && (
- <>
- {activation.inherit === 'OVERRIDES' && (
- <Tooltip
- overlay={translateWithParameters(
- 'coding_rules.overrides',
- selectedProfile.name,
- selectedProfile.parentName
- )}>
- <RuleInheritanceIcon
- className="little-spacer-left"
- inheritance={activation.inherit}
- />
- </Tooltip>
- )}
- {activation.inherit === 'INHERITED' && (
- <Tooltip
- overlay={translateWithParameters(
- 'coding_rules.inherits',
- selectedProfile.name,
- selectedProfile.parentName
- )}>
- <RuleInheritanceIcon
- className="little-spacer-left"
- inheritance={activation.inherit}
- />
- </Tooltip>
- )}
- </>
- )}
+ {selectedProfile && selectedProfile.parentName && (
+ <>
+ {activation.inherit === 'OVERRIDES' && (
+ <Tooltip
+ overlay={translateWithParameters(
+ 'coding_rules.overrides',
+ selectedProfile.name,
+ selectedProfile.parentName
+ )}>
+ <RuleInheritanceIcon
+ className="little-spacer-left"
+ inheritance={activation.inherit}
+ />
+ </Tooltip>
+ )}
+ {activation.inherit === 'INHERITED' && (
+ <Tooltip
+ overlay={translateWithParameters(
+ 'coding_rules.inherits',
+ selectedProfile.name,
+ selectedProfile.parentName
+ )}>
+ <RuleInheritanceIcon
+ className="little-spacer-left"
+ inheritance={activation.inherit}
+ />
+ </Tooltip>
+ )}
+ </>
+ )}
</td>
);
};
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/BulkChange-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/BulkChange-test.tsx
new file mode 100644
index 00000000000..1a22fdf85b2
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/BulkChange-test.tsx
@@ -0,0 +1,64 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 BulkChange from '../BulkChange';
+import { mockEvent, mockQualityProfile } from '../../../../helpers/testMocks';
+
+const profile = mockQualityProfile({
+ actions: {
+ edit: true,
+ setAsDefault: true,
+ copy: true,
+ associateProjects: true,
+ delete: false
+ }
+});
+
+it('should render correctly', () => {
+ const wrapper = shallowRender();
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('should not render anything', () => {
+ const wrapper = shallowRender({
+ referencedProfiles: { key: { ...profile, actions: { ...profile.actions, edit: false } } }
+ });
+ expect(wrapper.type()).toBeNull();
+});
+
+it('should display BulkChangeModal', () => {
+ const wrapper = shallowRender();
+ wrapper.instance().handleActivateClick(mockEvent());
+ expect(wrapper.find('BulkChangeModal')).toMatchSnapshot();
+});
+
+function shallowRender(props: Partial<BulkChange['props']> = {}) {
+ return shallow<BulkChange>(
+ <BulkChange
+ languages={{ js: { key: 'js', name: 'JavaScript' } }}
+ organization={undefined}
+ query={{ activation: false, profile: 'key' } as BulkChange['props']['query']}
+ referencedProfiles={{ key: profile }}
+ total={2}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetails-test.tsx b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetails-test.tsx
new file mode 100644
index 00000000000..3d2baaff55f
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/RuleDetails-test.tsx
@@ -0,0 +1,98 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2019 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 RuleDetails from '../RuleDetails';
+import { waitAndUpdate } from '../../../../helpers/testUtils';
+import { updateRule } from '../../../../api/rules';
+
+jest.mock('../../../../api/rules', () => ({
+ deleteRule: jest.fn(),
+ getRuleDetails: jest.fn().mockResolvedValue({
+ rule: getMockHelpers().mockRuleDetails(),
+ actives: [
+ {
+ qProfile: 'key',
+ inherit: 'NONE',
+ severity: 'MAJOR',
+ params: [],
+ createdAt: '2017-06-16T16:13:38+0200',
+ updatedAt: '2017-06-16T16:13:38+0200'
+ }
+ ]
+ }),
+ updateRule: jest.fn().mockResolvedValue({})
+}));
+
+const { mockQualityProfile } = getMockHelpers();
+const profile = mockQualityProfile();
+
+beforeEach(() => {
+ jest.clearAllMocks();
+});
+
+it('should render correctly', async () => {
+ const wrapper = shallowRender();
+ expect(wrapper).toMatchSnapshot();
+ await waitAndUpdate(wrapper);
+ expect(wrapper).toMatchSnapshot();
+});
+
+it('should correctly handle tag changes', async () => {
+ const wrapper = shallowRender();
+ await waitAndUpdate(wrapper);
+ wrapper.instance().handleTagsChange(['foo', 'bar']);
+ const ruleDetails = wrapper.state('ruleDetails');
+ expect(ruleDetails && ruleDetails.tags).toEqual(['foo', 'bar']);
+ await waitAndUpdate(wrapper);
+ expect(updateRule).toHaveBeenCalledWith({
+ key: 'squid:S1337',
+ organization: undefined,
+ tags: 'foo,bar'
+ });
+});
+
+function getMockHelpers() {
+ // We use this little "force-requiring" instead of an import statement in
+ // order to prevent a hoisting race condition while mocking. If we want to use
+ // a mock helper in a Jest mock, we have to require it like this. Otherwise,
+ // we get errors like:
+ // ReferenceError: testMocks_1 is not defined
+ return require.requireActual('../../../../helpers/testMocks');
+}
+
+function shallowRender(props: Partial<RuleDetails['props']> = {}) {
+ return shallow<RuleDetails>(
+ <RuleDetails
+ onActivate={jest.fn()}
+ onDeactivate={jest.fn()}
+ onDelete={jest.fn()}
+ onFilterChange={jest.fn()}
+ organization={undefined}
+ referencedProfiles={{ key: profile }}
+ referencedRepositories={{
+ javascript: { key: 'javascript', language: 'js', name: 'SonarAnalyzer' }
+ }}
+ ruleKey="squid:S1337"
+ selectedProfile={profile}
+ {...props}
+ />
+ );
+}
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/BulkChange-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/BulkChange-test.tsx.snap
new file mode 100644
index 00000000000..2cc9fb4b0d5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/BulkChange-test.tsx.snap
@@ -0,0 +1,99 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should display BulkChangeModal 1`] = `
+<BulkChangeModal
+ action="activate"
+ languages={
+ Object {
+ "js": Object {
+ "key": "js",
+ "name": "JavaScript",
+ },
+ }
+ }
+ onClose={[Function]}
+ query={
+ Object {
+ "activation": false,
+ "profile": "key",
+ }
+ }
+ referencedProfiles={
+ Object {
+ "key": Object {
+ "actions": Object {
+ "associateProjects": true,
+ "copy": true,
+ "delete": false,
+ "edit": true,
+ "setAsDefault": true,
+ },
+ "activeDeprecatedRuleCount": 2,
+ "activeRuleCount": 10,
+ "childrenCount": 0,
+ "depth": 1,
+ "isBuiltIn": false,
+ "isDefault": false,
+ "isInherited": false,
+ "key": "key",
+ "language": "js",
+ "languageName": "JavaScript",
+ "name": "name",
+ "organization": "foo",
+ "projectCount": 3,
+ },
+ }
+ }
+ total={2}
+/>
+`;
+
+exports[`should render correctly 1`] = `
+<Fragment>
+ <Dropdown
+ className="pull-left"
+ overlay={
+ <ul
+ className="menu"
+ >
+ <li>
+ <a
+ href="#"
+ onClick={[Function]}
+ >
+ coding_rules.activate_in
+ …
+ </a>
+ </li>
+ <li>
+ <a
+ href="#"
+ onClick={[Function]}
+ >
+ coding_rules.activate_in
+
+ <strong>
+ name
+ </strong>
+ </a>
+ </li>
+ <li>
+ <a
+ href="#"
+ onClick={[Function]}
+ >
+ coding_rules.deactivate_in
+ …
+ </a>
+ </li>
+ </ul>
+ }
+ >
+ <Button
+ className="js-bulk-change"
+ >
+ bulk_change
+ </Button>
+ </Dropdown>
+</Fragment>
+`;
diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetails-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetails-test.tsx.snap
new file mode 100644
index 00000000000..46c661b1a68
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetails-test.tsx.snap
@@ -0,0 +1,204 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<div
+ className="coding-rule-details"
+/>
+`;
+
+exports[`should render correctly 2`] = `
+<div
+ className="coding-rule-details"
+>
+ <DeferredSpinner
+ loading={false}
+ timeout={100}
+ >
+ <RuleDetailsMeta
+ onFilterChange={[MockFunction]}
+ onTagsChange={[Function]}
+ referencedRepositories={
+ Object {
+ "javascript": Object {
+ "key": "javascript",
+ "language": "js",
+ "name": "SonarAnalyzer",
+ },
+ }
+ }
+ ruleDetails={
+ Object {
+ "createdAt": "2014-12-16T17:26:54+0100",
+ "debtOverloaded": false,
+ "debtRemFnOffset": "5min",
+ "debtRemFnType": "CONSTANT_ISSUE",
+ "defaultDebtRemFnOffset": "5min",
+ "defaultDebtRemFnType": "CONSTANT_ISSUE",
+ "defaultRemFnBaseEffort": "5min",
+ "defaultRemFnType": "CONSTANT_ISSUE",
+ "htmlDesc": "",
+ "isExternal": false,
+ "isTemplate": false,
+ "key": "squid:S1337",
+ "lang": "java",
+ "langName": "Java",
+ "mdDesc": "",
+ "name": "\\".equals()\\" should not be used to test the values of \\"Atomic\\" classes",
+ "params": Array [],
+ "remFnBaseEffort": "5min",
+ "remFnOverloaded": false,
+ "remFnType": "CONSTANT_ISSUE",
+ "repo": "squid",
+ "scope": "MAIN",
+ "severity": "MAJOR",
+ "status": "READY",
+ "sysTags": Array [
+ "multi-threading",
+ ],
+ "tags": Array [],
+ "type": "BUG",
+ }
+ }
+ />
+ <RuleDetailsDescription
+ onChange={[Function]}
+ ruleDetails={
+ Object {
+ "createdAt": "2014-12-16T17:26:54+0100",
+ "debtOverloaded": false,
+ "debtRemFnOffset": "5min",
+ "debtRemFnType": "CONSTANT_ISSUE",
+ "defaultDebtRemFnOffset": "5min",
+ "defaultDebtRemFnType": "CONSTANT_ISSUE",
+ "defaultRemFnBaseEffort": "5min",
+ "defaultRemFnType": "CONSTANT_ISSUE",
+ "htmlDesc": "",
+ "isExternal": false,
+ "isTemplate": false,
+ "key": "squid:S1337",
+ "lang": "java",
+ "langName": "Java",
+ "mdDesc": "",
+ "name": "\\".equals()\\" should not be used to test the values of \\"Atomic\\" classes",
+ "params": Array [],
+ "remFnBaseEffort": "5min",
+ "remFnOverloaded": false,
+ "remFnType": "CONSTANT_ISSUE",
+ "repo": "squid",
+ "scope": "MAIN",
+ "severity": "MAJOR",
+ "status": "READY",
+ "sysTags": Array [
+ "multi-threading",
+ ],
+ "tags": Array [],
+ "type": "BUG",
+ }
+ }
+ />
+ <RuleDetailsProfiles
+ activations={
+ Array [
+ Object {
+ "createdAt": "2017-06-16T16:13:38+0200",
+ "inherit": "NONE",
+ "params": Array [],
+ "qProfile": "key",
+ "severity": "MAJOR",
+ "updatedAt": "2017-06-16T16:13:38+0200",
+ },
+ ]
+ }
+ onActivate={[Function]}
+ onDeactivate={[Function]}
+ referencedProfiles={
+ Object {
+ "key": Object {
+ "activeDeprecatedRuleCount": 2,
+ "activeRuleCount": 10,
+ "childrenCount": 0,
+ "depth": 1,
+ "isBuiltIn": false,
+ "isDefault": false,
+ "isInherited": false,
+ "key": "key",
+ "language": "js",
+ "languageName": "JavaScript",
+ "name": "name",
+ "organization": "foo",
+ "projectCount": 3,
+ },
+ }
+ }
+ ruleDetails={
+ Object {
+ "createdAt": "2014-12-16T17:26:54+0100",
+ "debtOverloaded": false,
+ "debtRemFnOffset": "5min",
+ "debtRemFnType": "CONSTANT_ISSUE",
+ "defaultDebtRemFnOffset": "5min",
+ "defaultDebtRemFnType": "CONSTANT_ISSUE",
+ "defaultRemFnBaseEffort": "5min",
+ "defaultRemFnType": "CONSTANT_ISSUE",
+ "htmlDesc": "",
+ "isExternal": false,
+ "isTemplate": false,
+ "key": "squid:S1337",
+ "lang": "java",
+ "langName": "Java",
+ "mdDesc": "",
+ "name": "\\".equals()\\" should not be used to test the values of \\"Atomic\\" classes",
+ "params": Array [],
+ "remFnBaseEffort": "5min",
+ "remFnOverloaded": false,
+ "remFnType": "CONSTANT_ISSUE",
+ "repo": "squid",
+ "scope": "MAIN",
+ "severity": "MAJOR",
+ "status": "READY",
+ "sysTags": Array [
+ "multi-threading",
+ ],
+ "tags": Array [],
+ "type": "BUG",
+ }
+ }
+ />
+ <Connect(withAppState(RuleDetailsIssues))
+ ruleDetails={
+ Object {
+ "createdAt": "2014-12-16T17:26:54+0100",
+ "debtOverloaded": false,
+ "debtRemFnOffset": "5min",
+ "debtRemFnType": "CONSTANT_ISSUE",
+ "defaultDebtRemFnOffset": "5min",
+ "defaultDebtRemFnType": "CONSTANT_ISSUE",
+ "defaultRemFnBaseEffort": "5min",
+ "defaultRemFnType": "CONSTANT_ISSUE",
+ "htmlDesc": "",
+ "isExternal": false,
+ "isTemplate": false,
+ "key": "squid:S1337",
+ "lang": "java",
+ "langName": "Java",
+ "mdDesc": "",
+ "name": "\\".equals()\\" should not be used to test the values of \\"Atomic\\" classes",
+ "params": Array [],
+ "remFnBaseEffort": "5min",
+ "remFnOverloaded": false,
+ "remFnType": "CONSTANT_ISSUE",
+ "repo": "squid",
+ "scope": "MAIN",
+ "severity": "MAJOR",
+ "status": "READY",
+ "sysTags": Array [
+ "multi-threading",
+ ],
+ "tags": Array [],
+ "type": "BUG",
+ }
+ }
+ />
+ </DeferredSpinner>
+</div>
+`;