)}
{!ruleDetails.isTemplate && (
- <RuleDetailsIssues organization={organization} ruleKey={ruleDetails.key} />
+ <RuleDetailsIssues organization={organization} ruleDetails={ruleDetails} />
)}
</DeferredSpinner>
</div>
import DeferredSpinner from '../../../components/common/DeferredSpinner';
import Tooltip from '../../../components/controls/Tooltip';
import { getFacet } from '../../../api/issues';
+import { RuleDetails } from '../../../app/types';
import { getIssuesUrl } from '../../../helpers/urls';
import { formatMeasure } from '../../../helpers/measures';
import { translate } from '../../../helpers/l10n';
interface Props {
organization: string | undefined;
- ruleKey: string;
+ ruleDetails: Pick<RuleDetails, 'key' | 'type'>;
}
interface Project {
}
componentDidUpdate(prevProps: Props) {
- if (prevProps.ruleKey !== this.props.ruleKey) {
+ if (prevProps.ruleDetails !== this.props.ruleDetails) {
this.fetchIssues();
}
}
this.mounted = false;
}
+ getBaseIssuesQuery = () => ({
+ resolved: 'false',
+ rules: this.props.ruleDetails.key,
+ types:
+ this.props.ruleDetails.type === 'SECURITY_HOTSPOT'
+ ? 'VULNERABILITY,SECURITY_HOTSPOT'
+ : undefined
+ });
+
fetchIssues = () => {
this.setState({ loading: true });
getFacet(
- { organization: this.props.organization, rules: this.props.ruleKey, resolved: false },
+ { ...this.getBaseIssuesQuery(), organization: this.props.organization },
'projects'
).then(
({ facet, response }) => {
if (total === undefined) {
return null;
}
- const path = getIssuesUrl(
- { resolved: 'false', rules: this.props.ruleKey },
- this.props.organization
- );
+ const path = getIssuesUrl(this.getBaseIssuesQuery(), this.props.organization);
const totalItem = (
<span className="little-spacer-left">
renderProject = (project: Project) => {
const path = getIssuesUrl(
- { projects: project.key, resolved: 'false', rules: this.props.ruleKey },
+ { ...this.getBaseIssuesQuery(), projects: project.key },
this.props.organization
);
return (
--- /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 RuleDetailsIssues from '../RuleDetailsIssues';
+import { waitAndUpdate } from '../../../../helpers/testUtils';
+import { getFacet } from '../../../../api/issues';
+
+jest.mock('../../../../api/issues', () => ({
+ getFacet: jest.fn().mockResolvedValue({
+ facet: [{ count: 13, val: 'sample-key' }, { count: 5, val: 'example-key' }],
+ response: {
+ components: [{ key: 'sample-key', name: 'Sample' }, { key: 'example-key', name: 'Example' }],
+ paging: { total: 18 }
+ }
+ })
+}));
+
+beforeEach(() => {
+ (getFacet as jest.Mock).mockClear();
+});
+
+it('should fetch issues and render', async () => {
+ await check('BUG', undefined);
+});
+
+it('should handle hotspot rules', async () => {
+ await check('SECURITY_HOTSPOT', ['VULNERABILITY', 'SECURITY_HOTSPOT']);
+});
+
+async function check(ruleType: string, requestedTypes: string[] | undefined) {
+ const wrapper = shallow(
+ <RuleDetailsIssues organization="org" ruleDetails={{ key: 'foo', type: ruleType }} />
+ );
+ await waitAndUpdate(wrapper);
+ expect(wrapper).toMatchSnapshot();
+ expect(getFacet).toBeCalledWith(
+ {
+ organization: 'org',
+ resolved: 'false',
+ rules: 'foo',
+ types: requestedTypes && requestedTypes.join()
+ },
+ 'projects'
+ );
+}
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should fetch issues and render 1`] = `
+<div
+ className="js-rule-issues coding-rule-section"
+>
+ <div
+ className="coding-rule-section-separator"
+ />
+ <DeferredSpinner
+ loading={false}
+ timeout={100}
+ >
+ <h3
+ className="coding-rules-detail-title"
+ >
+ coding_rules.issues
+ <span
+ className="little-spacer-left"
+ >
+ (
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/organizations/org/issues",
+ "query": Object {
+ "resolved": "false",
+ "rules": "foo",
+ "types": undefined,
+ },
+ }
+ }
+ >
+ 18
+ </Link>
+ )
+ </span>
+ </h3>
+ <table
+ className="coding-rules-detail-list coding-rules-most-violated-projects"
+ >
+ <tbody>
+ <tr>
+ <td
+ className="coding-rules-detail-list-name"
+ colSpan={2}
+ >
+ coding_rules.most_violating_projects
+ </td>
+ </tr>
+ <tr
+ key="sample-key"
+ >
+ <td
+ className="coding-rules-detail-list-name"
+ >
+ Sample
+ </td>
+ <td
+ className="coding-rules-detail-list-parameters"
+ >
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/organizations/org/issues",
+ "query": Object {
+ "projects": "sample-key",
+ "resolved": "false",
+ "rules": "foo",
+ "types": undefined,
+ },
+ }
+ }
+ >
+ 13
+ </Link>
+ </td>
+ </tr>
+ <tr
+ key="example-key"
+ >
+ <td
+ className="coding-rules-detail-list-name"
+ >
+ Example
+ </td>
+ <td
+ className="coding-rules-detail-list-parameters"
+ >
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/organizations/org/issues",
+ "query": Object {
+ "projects": "example-key",
+ "resolved": "false",
+ "rules": "foo",
+ "types": undefined,
+ },
+ }
+ }
+ >
+ 5
+ </Link>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </DeferredSpinner>
+</div>
+`;
+
+exports[`should handle hotspot rules 1`] = `
+<div
+ className="js-rule-issues coding-rule-section"
+>
+ <div
+ className="coding-rule-section-separator"
+ />
+ <DeferredSpinner
+ loading={false}
+ timeout={100}
+ >
+ <h3
+ className="coding-rules-detail-title"
+ >
+ coding_rules.issues
+ <span
+ className="little-spacer-left"
+ >
+ (
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/organizations/org/issues",
+ "query": Object {
+ "resolved": "false",
+ "rules": "foo",
+ "types": "VULNERABILITY,SECURITY_HOTSPOT",
+ },
+ }
+ }
+ >
+ 18
+ </Link>
+ )
+ </span>
+ </h3>
+ <table
+ className="coding-rules-detail-list coding-rules-most-violated-projects"
+ >
+ <tbody>
+ <tr>
+ <td
+ className="coding-rules-detail-list-name"
+ colSpan={2}
+ >
+ coding_rules.most_violating_projects
+ </td>
+ </tr>
+ <tr
+ key="sample-key"
+ >
+ <td
+ className="coding-rules-detail-list-name"
+ >
+ Sample
+ </td>
+ <td
+ className="coding-rules-detail-list-parameters"
+ >
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/organizations/org/issues",
+ "query": Object {
+ "projects": "sample-key",
+ "resolved": "false",
+ "rules": "foo",
+ "types": "VULNERABILITY,SECURITY_HOTSPOT",
+ },
+ }
+ }
+ >
+ 13
+ </Link>
+ </td>
+ </tr>
+ <tr
+ key="example-key"
+ >
+ <td
+ className="coding-rules-detail-list-name"
+ >
+ Example
+ </td>
+ <td
+ className="coding-rules-detail-list-parameters"
+ >
+ <Link
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/organizations/org/issues",
+ "query": Object {
+ "projects": "example-key",
+ "resolved": "false",
+ "rules": "foo",
+ "types": "VULNERABILITY,SECURITY_HOTSPOT",
+ },
+ }
+ }
+ >
+ 5
+ </Link>
+ </td>
+ </tr>
+ </tbody>
+ </table>
+ </DeferredSpinner>
+</div>
+`;