stats: T.Dict<number> | undefined;
}
-const RESOLUTIONS = ['', 'FIXED', 'FALSE-POSITIVE', 'WONTFIX', 'REMOVED'];
+const RESOLUTIONS = ['', 'FALSE-POSITIVE', 'FIXED', 'REMOVED', 'WONTFIX'];
export default class ResolutionFacet extends React.PureComponent<Props> {
property = 'resolutions';
statuses: string[];
}
-const STATUSES = ['OPEN', 'RESOLVED', 'REOPENED', 'CLOSED', 'CONFIRMED'];
+const STATUSES = ['OPEN', 'CONFIRMED', 'REOPENED', 'RESOLVED'];
+const HOTSPOT_STATUSES = ['TO_REVIEW', 'REVIEWED', 'IN_REVIEW'];
+const COMMON_STATUSES = ['CLOSED'];
export default class StatusFacet extends React.PureComponent<Props> {
property = 'statuses';
- static defaultProps = {
- open: true
- };
+ static defaultProps = { open: true };
handleItemClick = (itemValue: string, multiple: boolean) => {
const { statuses } = this.props;
<DeferredSpinner loading={this.props.fetching} />
{this.props.open && (
<>
- <FacetItemsList>{STATUSES.map(this.renderItem)}</FacetItemsList>
+ <FacetItemsList title={translate('issues')}>
+ {STATUSES.map(this.renderItem)}
+ </FacetItemsList>
+ <FacetItemsList title={translate('issue.type.SECURITY_HOTSPOT.plural')}>
+ {HOTSPOT_STATUSES.map(this.renderItem)}
+ </FacetItemsList>
+ <FacetItemsList title={translate('issues.issues_and_hotspots')}>
+ {COMMON_STATUSES.map(this.renderItem)}
+ </FacetItemsList>
<MultipleSelectionHint options={Object.keys(stats).length} values={statuses.length} />
</>
)}
--- /dev/null
+/*
+ * 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 StatusFacet from '../StatusFacet';
+import { click } from '../../../../helpers/testUtils';
+
+it('should render correctly', () => {
+ expect(shallowRender()).toMatchSnapshot();
+});
+
+it('should toggle status facet', () => {
+ const onToggle = jest.fn();
+ const wrapper = shallowRender({ onToggle });
+ click(wrapper.children('FacetHeader'));
+ expect(onToggle).toBeCalledWith('statuses');
+});
+
+it('should clear status facet', () => {
+ const onChange = jest.fn();
+ const wrapper = shallowRender({ onChange, statuses: ['TO_REVIEW'] });
+ wrapper.children('FacetHeader').prop<Function>('onClear')();
+ expect(onChange).toBeCalledWith({ statuses: [] });
+});
+
+it('should select a status', () => {
+ const onChange = jest.fn();
+ const wrapper = shallowRender({ onChange });
+ clickAndCheck('TO_REVIEW');
+ clickAndCheck('OPEN', true, ['OPEN', 'TO_REVIEW']);
+ clickAndCheck('CONFIRMED');
+
+ function clickAndCheck(status: string, multiple = false, expected = [status]) {
+ wrapper
+ .find(`FacetItemsList`)
+ .find(`FacetItem[value="${status}"]`)
+ .prop<Function>('onClick')(status, multiple);
+ expect(onChange).lastCalledWith({ statuses: expected });
+ wrapper.setProps({ statuses: expected });
+ }
+});
+
+function shallowRender(props: Partial<StatusFacet['props']> = {}) {
+ return shallow(
+ <StatusFacet
+ fetching={false}
+ onChange={jest.fn()}
+ onToggle={jest.fn()}
+ open={true}
+ stats={{
+ OPEN: 104,
+ CONFIRMED: 8,
+ REOPENED: 0,
+ RESOLVED: 0,
+ CLOSED: 8,
+ TO_REVIEW: 150,
+ IN_REVIEW: 7,
+ REVIEWED: 1105
+ }}
+ statuses={[]}
+ {...props}
+ />
+ );
+}
--- /dev/null
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<FacetBox
+ property="statuses"
+>
+ <FacetHeader
+ name="issues.facet.statuses"
+ onClear={[Function]}
+ onClick={[Function]}
+ open={true}
+ values={Array []}
+ />
+ <DeferredSpinner
+ loading={false}
+ timeout={100}
+ />
+ <FacetItemsList
+ title="issues"
+ >
+ <FacetItem
+ active={false}
+ disabled={false}
+ halfWidth={true}
+ key="OPEN"
+ loading={false}
+ name={
+ <StatusHelper
+ status="OPEN"
+ />
+ }
+ onClick={[Function]}
+ stat="104"
+ tooltip="issue.status.OPEN"
+ value="OPEN"
+ />
+ <FacetItem
+ active={false}
+ disabled={false}
+ halfWidth={true}
+ key="CONFIRMED"
+ loading={false}
+ name={
+ <StatusHelper
+ status="CONFIRMED"
+ />
+ }
+ onClick={[Function]}
+ stat="8"
+ tooltip="issue.status.CONFIRMED"
+ value="CONFIRMED"
+ />
+ <FacetItem
+ active={false}
+ disabled={true}
+ halfWidth={true}
+ key="REOPENED"
+ loading={false}
+ name={
+ <StatusHelper
+ status="REOPENED"
+ />
+ }
+ onClick={[Function]}
+ stat={0}
+ tooltip="issue.status.REOPENED"
+ value="REOPENED"
+ />
+ <FacetItem
+ active={false}
+ disabled={true}
+ halfWidth={true}
+ key="RESOLVED"
+ loading={false}
+ name={
+ <StatusHelper
+ status="RESOLVED"
+ />
+ }
+ onClick={[Function]}
+ stat={0}
+ tooltip="issue.status.RESOLVED"
+ value="RESOLVED"
+ />
+ </FacetItemsList>
+ <FacetItemsList
+ title="issue.type.SECURITY_HOTSPOT.plural"
+ >
+ <FacetItem
+ active={false}
+ disabled={false}
+ halfWidth={true}
+ key="TO_REVIEW"
+ loading={false}
+ name={
+ <StatusHelper
+ status="TO_REVIEW"
+ />
+ }
+ onClick={[Function]}
+ stat="150"
+ tooltip="issue.status.TO_REVIEW"
+ value="TO_REVIEW"
+ />
+ <FacetItem
+ active={false}
+ disabled={false}
+ halfWidth={true}
+ key="REVIEWED"
+ loading={false}
+ name={
+ <StatusHelper
+ status="REVIEWED"
+ />
+ }
+ onClick={[Function]}
+ stat="1.1short_number_suffix.k"
+ tooltip="issue.status.REVIEWED"
+ value="REVIEWED"
+ />
+ <FacetItem
+ active={false}
+ disabled={false}
+ halfWidth={true}
+ key="IN_REVIEW"
+ loading={false}
+ name={
+ <StatusHelper
+ status="IN_REVIEW"
+ />
+ }
+ onClick={[Function]}
+ stat="7"
+ tooltip="issue.status.IN_REVIEW"
+ value="IN_REVIEW"
+ />
+ </FacetItemsList>
+ <FacetItemsList
+ title="issues.issues_and_hotspots"
+ >
+ <FacetItem
+ active={false}
+ disabled={false}
+ halfWidth={true}
+ key="CLOSED"
+ loading={false}
+ name={
+ <StatusHelper
+ status="CLOSED"
+ />
+ }
+ onClick={[Function]}
+ stat="8"
+ tooltip="issue.status.CLOSED"
+ value="CLOSED"
+ />
+ </FacetItemsList>
+ <MultipleSelectionHint
+ options={8}
+ values={0}
+ />
+</FacetBox>
+`;
interface Props {
children?: React.ReactNode;
+ title?: string;
}
-export default function FacetItemsList(props: Props) {
- return <div className="search-navigator-facet-list">{props.children}</div>;
+export default function FacetItemsList({ children, title }: Props) {
+ return (
+ <div className="search-navigator-facet-list">
+ {title && <div className="search-navigator-facet-list-title">{title}</div>}
+ {children}
+ </div>
+ );
}
)
).toMatchSnapshot();
});
+
+it('should render with title', () => {
+ expect(
+ shallow(
+ <FacetItemsList title="title test">
+ <div />
+ </FacetItemsList>
+ )
+ ).toMatchSnapshot();
+});
<div />
</div>
`;
+
+exports[`should render with title 1`] = `
+<div
+ className="search-navigator-facet-list"
+>
+ <div
+ className="search-navigator-facet-list-title"
+ >
+ title test
+ </div>
+ <div />
+</div>
+`;
confirmed: ConfirmedStatusIcon,
reopened: ReopenedStatusIcon,
resolved: ResolvedStatusIcon,
- closed: ClosedStatusIcon
+ closed: ClosedStatusIcon,
+ to_review: OpenStatusIcon,
+ in_review: ConfirmedStatusIcon,
+ reviewed: ResolvedStatusIcon
};
export default function StatusIcon(props: Props) {
font-size: 0;
}
+.search-navigator-facet-list-title {
+ margin: 0 var(--gridSize) calc(var(--gridSize) / 2);
+ font-size: var(--smallFontSize);
+ font-weight: bold;
+}
+
+.search-navigator-facet-list + .search-navigator-facet-list > .search-navigator-facet-list-title {
+ border-top: 1px solid var(--barBorderColor);
+ padding-top: var(--gridSize);
+}
+
.search-navigator-facet-empty {
margin: 0 0 0 0;
padding: 0 10px 10px;
issue.type.SECURITY_HOTSPOT.plural=Security Hotspots
issue.status.REOPENED=Reopened
-issue.status.REOPENED.description=Transitioned to and then back from some other status.
issue.status.RESOLVED=Resolved
-issue.status.RESOLVED.description=Manually marked as corrected.
issue.status.OPEN=Open
-issue.status.OPEN.description=Untouched. This status is set automatically at issue creation.
issue.status.CONFIRMED=Confirmed
-issue.status.CONFIRMED.description=Manually examined and affirmed as an issue that needs attention.
issue.status.CLOSED=Closed
-issue.status.CLOSED.description=Non-active and no longer requiring attention.
-issue.status.TOREVIEW=To Review
-issue.status.TOREVIEW.description=A review is required to check for a vulnerability.
+issue.status.TO_REVIEW=To Review
+issue.status.IN_REVIEW=In Review
+issue.status.REVIEWED=Reviewed
issue.resolution.FALSE-POSITIVE=False Positive
issue.resolution.FALSE-POSITIVE.description=Issues that manual review determined were False Positives. Effort from these issues is ignored.
issue.resolution.FIXED=Fixed
issue.resolution.FIXED.description=Issues that were corrected in code and reanalyzed.
-issue.resolution.WONTFIX=Won't fix
+issue.resolution.WONTFIX=Won't Fix
issue.resolution.WONTFIX.description=Issues that are accepted in this context. They and their effort will be ignored.
issue.resolution.REMOVED=Removed
issue.resolution.REMOVED.description=Either the rule or the resource was changed (removed, relocated, parameters changed, etc.) so that analysis no longer finds these issues.
issues.no_my_issues=There are no issues assigned to you.
issues.no_issues=No Issues. Hooray!
issues.x_more_locations=+ {0} more location(s)
+issues.issues_and_hotspots=Issues & Security Hotspots
issues.hotspots.helper=Security Hotspots aren't necessarily issues, but they need to be reviewed to make sure they aren't vulnerabilities.
issues.facet.severities=Severity
issues.facet.projects=Project
issues.facet.statuses=Status
+issues.facet.hotspotStatuses=Hotspot Status
issues.facet.assignees=Assignee
issues.facet.files=File
issues.facet.modules=Module
projects_role.issueadmin=Administer Issues
projects_role.issueadmin.desc=Change the type and severity of issues, resolve issues as being "won't fix" or "false-positive" (users also need "Browse" permission).
projects_role.securityhotspotadmin=Administer Security Hotspots
-projects_role.securityhotspotadmin.desc=Detect a Vulnerability from a Security Hotspot. Reject, clear, accept, reopen a Security Hotspot (users also need "Browse" permissions).
+projects_role.securityhotspotadmin.desc=Open a Vulnerability from a Security Hotspot. Resolved a Security Hotspot as reviewed, set it as in review or reset it as to review (users also need Browse permission).
projects_role.user=Browse
projects_role.user.desc=Access a project, browse its measures and issues, confirm or resolve issues as "fixed", change the assignee, comment on issues and change tags.
projects_role.codeviewer=See Source Code