import FacetItem from '../../../components/facet/FacetItem';
import FacetItemsList from '../../../components/facet/FacetItemsList';
import ListStyleFacet from '../../../components/facet/ListStyleFacet';
+import ListStyleFacetFooter from '../../../components/facet/ListStyleFacetFooter';
import MultipleSelectionHint from '../../../components/facet/MultipleSelectionHint';
import { translate } from '../../../helpers/l10n';
import { highlightTerm } from '../../../helpers/search';
interface State {
standards: Standards;
+ showFullSonarSourceList: boolean;
}
type StatsProp =
| 'sonarsourceSecurityStats';
type ValuesProp = StandardType;
+const INITIAL_FACET_COUNT = 15;
export default class StandardFacet extends React.PureComponent<Props, State> {
mounted = false;
property = STANDARDS;
state: State = {
+ showFullSonarSourceList: false,
standards: {
owaspTop10: {},
'owaspTop10-2021': {},
}
renderSonarSourceSecurityList() {
- return this.renderList(
- 'sonarsourceSecurityStats',
- SecurityStandard.SONARSOURCE,
- renderSonarSourceSecurityCategory,
- this.handleSonarSourceSecurityItemClick
+ const stats = this.props.sonarsourceSecurityStats;
+ const values = this.props.sonarsourceSecurity;
+
+ if (!stats) {
+ return null;
+ }
+
+ const sortedItems = sortBy(
+ Object.keys(stats),
+ key => -stats[key],
+ key => renderSonarSourceSecurityCategory(this.state.standards, key)
+ );
+
+ const limitedList = this.state.showFullSonarSourceList
+ ? sortedItems
+ : sortedItems.slice(0, INITIAL_FACET_COUNT);
+
+ // make sure all selected items are displayed
+ const selectedBelowLimit = this.state.showFullSonarSourceList
+ ? []
+ : sortedItems.slice(INITIAL_FACET_COUNT).filter(item => values.includes(item));
+
+ const allItemShown = limitedList.length + selectedBelowLimit.length === sortedItems.length;
+ return (
+ <>
+ <FacetItemsList>
+ {limitedList.map(item => (
+ <FacetItem
+ active={values.includes(item)}
+ key={item}
+ name={renderSonarSourceSecurityCategory(this.state.standards, item)}
+ onClick={this.handleSonarSourceSecurityItemClick}
+ stat={formatFacetStat(stats[item])}
+ tooltip={renderSonarSourceSecurityCategory(this.state.standards, item)}
+ value={item}
+ />
+ ))}
+ </FacetItemsList>
+ {selectedBelowLimit.length > 0 && (
+ <>
+ {!allItemShown && <div className="note spacer-bottom text-center">⋯</div>}
+ <FacetItemsList>
+ {selectedBelowLimit.map(item => (
+ <FacetItem
+ active={true}
+ key={item}
+ name={renderSonarSourceSecurityCategory(this.state.standards, item)}
+ onClick={this.handleSonarSourceSecurityItemClick}
+ stat={formatFacetStat(stats[item])}
+ tooltip={renderSonarSourceSecurityCategory(this.state.standards, item)}
+ value={item}
+ />
+ ))}
+ </FacetItemsList>
+ </>
+ )}
+ {!allItemShown && (
+ <ListStyleFacetFooter
+ count={limitedList.length + selectedBelowLimit.length}
+ showLess={
+ this.state.showFullSonarSourceList
+ ? () => this.setState({ showFullSonarSourceList: false })
+ : undefined
+ }
+ showMore={() => this.setState({ showFullSonarSourceList: true })}
+ total={sortedItems.length}
+ />
+ )}
+ </>
);
}
*/
import { shallow } from 'enzyme';
import * as React from 'react';
+import ListStyleFacetFooter from '../../../../components/facet/ListStyleFacetFooter';
import { getStandards } from '../../../../helpers/security-standard';
import { click } from '../../../../helpers/testUtils';
import { Query } from '../../utils';
title: 'Broken Authentication'
}
},
+ 'owaspTop10-2021': {
+ a1: {
+ title: 'Injection'
+ },
+ a2: {
+ title: 'Broken Authentication'
+ }
+ },
sansTop25: {
'insecure-interaction': {
title: 'Insecure Interaction Between Components'
expect(getStandards).toBeCalled();
});
+it('should show sonarsource facet more button', () => {
+ const wrapper = shallowRender({
+ open: true,
+ sonarsourceSecurity: ['traceability', 'permission', 'others'],
+ sonarsourceSecurityOpen: true,
+ sonarsourceSecurityStats: {
+ 'buffer-overflow': 3,
+ 'sql-injection': 3,
+ rce: 3,
+ 'object-injection': 3,
+ 'command-injection': 3,
+ 'path-traversal-injection': 3,
+ 'ldap-injection': 3,
+ 'xpath-injection': 3,
+ 'expression-lang-injection': 3,
+ 'log-injection': 3,
+ xxe: 3,
+ xss: 3,
+ dos: 3,
+ ssrf: 3,
+ csrf: 3,
+ 'http-response-splitting': 3,
+ 'open-redirect': 3,
+ 'weak-cryptography': 3,
+ auth: 3,
+ 'insecure-conf': 3,
+ 'file-manipulation': 3,
+ 'encrypt-data': 3,
+ traceability: 3,
+ permission: 3,
+ others: 3
+ }
+ });
+
+ expect(wrapper.find(ListStyleFacetFooter).exists()).toBe(true);
+
+ wrapper.setState({ showFullSonarSourceList: true });
+ expect(wrapper.find(ListStyleFacetFooter).exists()).toBe(false);
+});
+
it('should render empty sub-facet', () => {
expect(
shallowRender({ open: true, sansTop25: [], sansTop25Open: true, sansTop25Stats: {} }).find(