From 8f7315c0ca69439a727776ca6ed8ae2185bf851a Mon Sep 17 00:00:00 2001 From: =?utf8?q?S=C3=A9bastien=20Lesaint?= Date: Mon, 23 Dec 2019 15:49:31 +0100 Subject: [PATCH] SONAR-12723 remove hotspots from issues page --- .../js/apps/issues/__tests__/utils-test.ts | 27 ---- .../main/js/apps/issues/components/App.tsx | 4 +- .../issues/components/__tests__/App-test.tsx | 28 +---- .../js/apps/issues/sidebar/StatusFacet.tsx | 10 +- .../main/js/apps/issues/sidebar/TypeFacet.tsx | 68 +---------- .../sidebar/__tests__/StatusFacet-test.tsx | 13 +- .../sidebar/__tests__/TypeFacet-test.tsx | 33 +---- .../__snapshots__/Sidebar-test.tsx.snap | 12 +- .../__snapshots__/StatusFacet-test.tsx.snap | 58 +-------- .../__snapshots__/TypeFacet-test.tsx.snap | 115 ------------------ .../src/main/js/apps/issues/utils.ts | 12 +- server/sonar-web/src/main/js/types/types.d.ts | 5 +- .../resources/org/sonar/l10n/core.properties | 3 - 13 files changed, 25 insertions(+), 363 deletions(-) diff --git a/server/sonar-web/src/main/js/apps/issues/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/issues/__tests__/utils-test.ts index 93db9b8dfb1..a88f0031c23 100644 --- a/server/sonar-web/src/main/js/apps/issues/__tests__/utils-test.ts +++ b/server/sonar-web/src/main/js/apps/issues/__tests__/utils-test.ts @@ -20,7 +20,6 @@ import { scrollToElement } from 'sonar-ui-common/helpers/scrolling'; import { scrollToIssue, - shouldOpenSeverityFacet, shouldOpenSonarSourceSecurityFacet, shouldOpenStandardsChildFacet, shouldOpenStandardsFacet @@ -62,34 +61,11 @@ describe('scrollToIssue', () => { }); }); -describe('shouldOpenSeverityFacet', () => { - it('should open severity facet', () => { - expect(shouldOpenSeverityFacet({ severities: true }, { types: [] })).toBe(true); - expect(shouldOpenSeverityFacet({}, { types: [] })).toBe(true); - expect(shouldOpenSeverityFacet({}, { types: ['VULNERABILITY'] })).toBe(true); - expect(shouldOpenSeverityFacet({ severities: false }, { types: ['VULNERABILITY'] })).toBe(true); - expect(shouldOpenSeverityFacet({ severities: false }, { types: [] })).toBe(true); - expect(shouldOpenSeverityFacet({}, { types: ['BUGS', 'SECURITY_HOTSPOT'] })).toBe(true); - expect(shouldOpenSeverityFacet({ severities: true }, { types: ['SECURITY_HOTSPOT'] })).toBe( - true - ); - }); - - it('should NOT open severity facet', () => { - expect(shouldOpenSeverityFacet({}, { types: ['SECURITY_HOTSPOT'] })).toBe(false); - }); -}); - describe('shouldOpenStandardsFacet', () => { it('should open standard facet', () => { expect(shouldOpenStandardsFacet({ standards: true }, { types: [] })).toBe(true); expect(shouldOpenStandardsFacet({ owaspTop10: true }, { types: [] })).toBe(true); expect(shouldOpenStandardsFacet({}, { types: ['VULNERABILITY'] })).toBe(true); - expect(shouldOpenStandardsFacet({}, { types: ['SECURITY_HOTSPOT'] })).toBe(true); - expect(shouldOpenStandardsFacet({}, { types: ['VULNERABILITY', 'SECURITY_HOTSPOT'] })).toBe( - true - ); - expect(shouldOpenStandardsFacet({}, { types: ['BUGS', 'SECURITY_HOTSPOT'] })).toBe(true); expect(shouldOpenStandardsFacet({ standards: false }, { types: ['VULNERABILITY'] })).toBe(true); }); @@ -130,9 +106,6 @@ describe('shouldOpenStandardsChildFacet', () => { expect(shouldOpenStandardsChildFacet({}, { types: ['VULNERABILITY'] }, 'sansTop25')).toBe( false ); - expect( - shouldOpenStandardsChildFacet({}, { types: ['SECURITY_HOTSPOT'] }, 'sonarsourceSecurity') - ).toBe(false); expect( shouldOpenStandardsChildFacet( {}, diff --git a/server/sonar-web/src/main/js/apps/issues/components/App.tsx b/server/sonar-web/src/main/js/apps/issues/components/App.tsx index 237aae53a18..cb4c4eac287 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/App.tsx +++ b/server/sonar-web/src/main/js/apps/issues/components/App.tsx @@ -73,7 +73,6 @@ import { saveMyIssues, scrollToIssue, serializeQuery, - shouldOpenSeverityFacet, shouldOpenSonarSourceSecurityFacet, shouldOpenStandardsChildFacet, shouldOpenStandardsFacet, @@ -164,7 +163,7 @@ export class App extends React.PureComponent { openFacets: { owaspTop10: shouldOpenStandardsChildFacet({}, query, 'owaspTop10'), sansTop25: shouldOpenStandardsChildFacet({}, query, 'sansTop25'), - severities: shouldOpenSeverityFacet({}, query), + severities: true, sonarsourceSecurity: shouldOpenSonarSourceSecurityFacet({}, query), standards: shouldOpenStandardsFacet({}, query), types: true @@ -680,7 +679,6 @@ export class App extends React.PureComponent { this.setState(({ openFacets }) => ({ openFacets: { ...openFacets, - severities: shouldOpenSeverityFacet(openFacets, changes), sonarsourceSecurity: shouldOpenSonarSourceSecurityFacet(openFacets, changes), standards: shouldOpenStandardsFacet(openFacets, changes) } diff --git a/server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx b/server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx index 51bb8e1ba29..ea42ad2518e 100644 --- a/server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx +++ b/server/sonar-web/src/main/js/apps/issues/components/__tests__/App-test.tsx @@ -238,26 +238,14 @@ it('should fetch issues for component', async () => { it('should display the right facets open', () => { expect( shallowRender({ - location: mockLocation({ query: { types: 'SECURITY_HOTSPOT' } }) - }).state('openFacets') - ).toEqual({ - owaspTop10: false, - sansTop25: false, - severities: false, - standards: true, - sonarsourceSecurity: true, - types: true - }); - expect( - shallowRender({ - location: mockLocation({ query: { types: 'BUGS,SECURITY_HOTSPOT' } }) + location: mockLocation({ query: { types: 'BUGS' } }) }).state('openFacets') ).toEqual({ owaspTop10: false, sansTop25: false, severities: true, - standards: true, - sonarsourceSecurity: true, + standards: false, + sonarsourceSecurity: false, types: true }); expect( @@ -281,7 +269,6 @@ it('should correctly handle filter changes', () => { instance.handleFilterChange({ types: ['VULNERABILITY'] }); expect(instance.state.openFacets).toEqual({ types: true, - severities: true, sonarsourceSecurity: true, standards: true }); @@ -289,15 +276,6 @@ it('should correctly handle filter changes', () => { instance.handleFilterChange({ types: ['BUGS'] }); expect(instance.state.openFacets).toEqual({ types: true, - severities: true, - sonarsourceSecurity: true, - standards: true - }); - instance.setState({ openFacets: { types: true } }); - instance.handleFilterChange({ types: ['SECURITY_HOTSPOT'] }); - expect(instance.state.openFacets).toEqual({ - types: true, - severities: false, sonarsourceSecurity: true, standards: true }); diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/StatusFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/StatusFacet.tsx index 3b6d9bd1fa9..f4f5725c997 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/StatusFacet.tsx +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/StatusFacet.tsx @@ -37,9 +37,7 @@ interface Props { statuses: string[]; } -const STATUSES = ['OPEN', 'CONFIRMED', 'REOPENED', 'RESOLVED']; -const HOTSPOT_STATUSES = ['TO_REVIEW', 'REVIEWED', 'IN_REVIEW']; -const COMMON_STATUSES = ['CLOSED']; +const STATUSES = ['OPEN', 'CONFIRMED', 'REOPENED', 'RESOLVED', 'CLOSED']; export default class StatusFacet extends React.PureComponent { property = 'statuses'; @@ -112,12 +110,6 @@ export default class StatusFacet extends React.PureComponent { {STATUSES.map(this.renderItem)} - - {HOTSPOT_STATUSES.map(this.renderItem)} - - - {COMMON_STATUSES.map(this.renderItem)} - )} diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/TypeFacet.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/TypeFacet.tsx index 746187fdfbb..f7513f5879f 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/TypeFacet.tsx +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/TypeFacet.tsx @@ -19,11 +19,7 @@ */ import { orderBy, without } from 'lodash'; import * as React from 'react'; -import { connect } from 'react-redux'; -import { Link } from 'react-router'; -import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip'; import IssueTypeIcon from 'sonar-ui-common/components/icons/IssueTypeIcon'; -import NewsBox from 'sonar-ui-common/components/ui/NewsBox'; import { translate } from 'sonar-ui-common/helpers/l10n'; import FacetBox from '../../../components/facet/FacetBox'; import FacetHeader from '../../../components/facet/FacetHeader'; @@ -31,22 +27,18 @@ import FacetItem from '../../../components/facet/FacetItem'; import FacetItemsList from '../../../components/facet/FacetItemsList'; import MultipleSelectionHint from '../../../components/facet/MultipleSelectionHint'; import { ISSUE_TYPES } from '../../../helpers/constants'; -import { getCurrentUser, getCurrentUserSetting, Store } from '../../../store/rootReducer'; -import { setCurrentUserSetting } from '../../../store/users'; import { formatFacetStat, Query } from '../utils'; interface Props { fetching: boolean; - newsBoxDismissHotspots?: boolean; onChange: (changes: Partial) => void; onToggle: (property: string) => void; open: boolean; - setCurrentUserSetting: (setting: T.CurrentUserSetting) => void; stats: T.Dict | undefined; types: string[]; } -export class TypeFacet extends React.PureComponent { +export default class TypeFacet extends React.PureComponent { property = 'types'; static defaultProps = { @@ -75,10 +67,6 @@ export class TypeFacet extends React.PureComponent { this.props.onChange({ [this.property]: [] }); }; - handleDismiss = () => { - this.props.setCurrentUserSetting({ key: 'newsbox.dismiss.hotspots', value: 'true' }); - }; - getStat(type: string) { const { stats } = this.props; return stats ? stats[type] : undefined; @@ -88,10 +76,6 @@ export class TypeFacet extends React.PureComponent { return this.props.types.includes(type); } - stopPropagation = (event: React.MouseEvent) => { - event.stopPropagation(); - }; - renderItem = (type: string) => { const active = this.isFacetItemActive(type); const stat = this.getStat(type); @@ -105,23 +89,6 @@ export class TypeFacet extends React.PureComponent { {' '} {translate('issue.type', type)} - {type === 'SECURITY_HOTSPOT' && this.props.newsBoxDismissHotspots && ( - -

{translate('issues.hotspots.helper')}

-
- - {translate('learn_more')} - - - } - /> - )}
} onClick={this.handleItemClick} @@ -132,12 +99,9 @@ export class TypeFacet extends React.PureComponent { }; render() { - const { newsBoxDismissHotspots, types, stats = {} } = this.props; + const { types, stats = {} } = this.props; const values = types.map(type => translate('issue.type', type)); - const showHotspotNewsBox = - types.includes('SECURITY_HOTSPOT') || (types.length === 0 && stats['SECURITY_HOTSPOT'] > 0); - return ( { {this.props.open && ( <> - {ISSUE_TYPES.map(this.renderItem)} - {!newsBoxDismissHotspots && showHotspotNewsBox && ( - -

{translate('issues.hotspots.helper')}

-

- - {translate('learn_more')} - -

-
- )} + + {ISSUE_TYPES.filter(t => t !== 'SECURITY_HOTSPOT').map(this.renderItem)} + )} @@ -171,15 +125,3 @@ export class TypeFacet extends React.PureComponent { ); } } - -const mapStateToProps = (state: Store) => ({ - newsBoxDismissHotspots: - !getCurrentUser(state).isLoggedIn || - getCurrentUserSetting(state, 'newsbox.dismiss.hotspots') === 'true' -}); - -const mapDispatchToProps = { - setCurrentUserSetting -}; - -export default connect(mapStateToProps, mapDispatchToProps)(TypeFacet); diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/StatusFacet-test.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/StatusFacet-test.tsx index 9798f9ac2fd..a64d18ba279 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/StatusFacet-test.tsx +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/StatusFacet-test.tsx @@ -35,7 +35,7 @@ it('should toggle status facet', () => { it('should clear status facet', () => { const onChange = jest.fn(); - const wrapper = shallowRender({ onChange, statuses: ['TO_REVIEW'] }); + const wrapper = shallowRender({ onChange, statuses: ['CONFIRMED'] }); wrapper.children('FacetHeader').prop('onClear')(); expect(onChange).toBeCalledWith({ statuses: [] }); }); @@ -43,9 +43,9 @@ it('should clear status facet', () => { it('should select a status', () => { const onChange = jest.fn(); const wrapper = shallowRender({ onChange }); - clickAndCheck('TO_REVIEW'); - clickAndCheck('OPEN', true, ['OPEN', 'TO_REVIEW']); - clickAndCheck('CONFIRMED'); + clickAndCheck('OPEN'); + clickAndCheck('CONFIRMED', true, ['CONFIRMED', 'OPEN']); + clickAndCheck('CLOSED'); function clickAndCheck(status: string, multiple = false, expected = [status]) { wrapper @@ -69,10 +69,7 @@ function shallowRender(props: Partial = {}) { CONFIRMED: 8, REOPENED: 0, RESOLVED: 0, - CLOSED: 8, - TO_REVIEW: 150, - IN_REVIEW: 7, - REVIEWED: 1105 + CLOSED: 8 }} statuses={[]} {...props} diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/TypeFacet-test.tsx b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/TypeFacet-test.tsx index 4894130109f..5c4f246f757 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/TypeFacet-test.tsx +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/TypeFacet-test.tsx @@ -20,7 +20,7 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { click } from 'sonar-ui-common/helpers/testUtils'; -import { TypeFacet } from '../TypeFacet'; +import TypeFacet from '../TypeFacet'; it('should render open by default', () => { expect(shallowRender({ types: ['VULNERABILITY', 'CODE_SMELL'] })).toMatchSnapshot(); @@ -45,7 +45,6 @@ it('should select a type', () => { const wrapper = shallowRender({ onChange }); clickAndCheck('CODE_SMELL'); clickAndCheck('VULNERABILITY', true, ['CODE_SMELL', 'VULNERABILITY']); - clickAndCheck('SECURITY_HOTSPOT'); function clickAndCheck(type: string, multiple = false, expected = [type]) { wrapper @@ -57,42 +56,12 @@ it('should select a type', () => { } }); -it('should display the hotspot newsbox', () => { - expect(shallowRender({ types: ['SECURITY_HOTSPOT'] }).find('NewsBox')).toMatchSnapshot(); - expect( - shallowRender({ types: [] }) - .find('NewsBox') - .exists() - ).toBe(true); -}); - -it('should display the hotspot tooltip helper only', () => { - let wrapper = shallowRender({ types: ['SECURITY_HOTSPOT'], newsBoxDismissHotspots: true }); - expect(wrapper.find('NewsBox').exists()).toBe(false); - expect( - wrapper - .find(`FacetItemsList`) - .find(`FacetItem[value="SECURITY_HOTSPOT"]`) - .prop('name') - ).toMatchSnapshot(); - - wrapper = shallowRender({ types: ['BUGS'], newsBoxDismissHotspots: true }); - expect(wrapper.find('NewsBox').exists()).toBe(false); - expect( - wrapper - .find(`FacetItemsList`) - .find(`FacetItem[value="SECURITY_HOTSPOT"]`) - .prop('name') - ).toMatchSnapshot(); -}); - function shallowRender(props: Partial = {}) { return shallow( - - - - } - onClick={[Function]} - stat="150" - tooltip="issue.status.TO_REVIEW" - value="TO_REVIEW" - /> - - } - onClick={[Function]} - stat="1.1short_number_suffix.k" - tooltip="issue.status.REVIEWED" - value="REVIEWED" - /> - - } - onClick={[Function]} - stat="7" - tooltip="issue.status.IN_REVIEW" - value="IN_REVIEW" - /> - -
diff --git a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/TypeFacet-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/TypeFacet-test.tsx.snap index e237e5bc5f8..534929cfa43 100644 --- a/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/TypeFacet-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/issues/sidebar/__tests__/__snapshots__/TypeFacet-test.tsx.snap @@ -1,98 +1,5 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should display the hotspot newsbox 1`] = ` - -

- issues.hotspots.helper -

-

- - learn_more - -

-
-`; - -exports[`should display the hotspot tooltip helper only 1`] = ` - - - - issue.type.SECURITY_HOTSPOT - -

- issues.hotspots.helper -

-
- - learn_more - - - } - /> -
-`; - -exports[`should display the hotspot tooltip helper only 2`] = ` - - - - issue.type.SECURITY_HOTSPOT - -

- issues.hotspots.helper -

-
- - learn_more - - - } - /> -
-`; - exports[`should render open by default 1`] = ` - - - - issue.type.SECURITY_HOTSPOT - - } - onClick={[Function]} - stat="1" - value="SECURITY_HOTSPOT" - /> , query: Partial) { - return ( - openFacets.severities || - !(query.types && query.types.length === 1 && query.types[0] === 'SECURITY_HOTSPOT') - ); -} - export function shouldOpenStandardsFacet( openFacets: T.Dict, query: Partial @@ -316,10 +309,7 @@ export function shouldOpenSonarSourceSecurityFacet( } function isFilteredBySecurityIssueTypes(query: Partial): boolean { - return ( - query.types !== undefined && - (query.types.includes('SECURITY_HOTSPOT') || query.types.includes('VULNERABILITY')) - ); + return query.types !== undefined && query.types.includes('VULNERABILITY'); } function isOneStandardChildFacetOpen(openFacets: T.Dict, query: Partial): boolean { diff --git a/server/sonar-web/src/main/js/types/types.d.ts b/server/sonar-web/src/main/js/types/types.d.ts index 0dc02ac6b69..c951f28def9 100644 --- a/server/sonar-web/src/main/js/types/types.d.ts +++ b/server/sonar-web/src/main/js/types/types.d.ts @@ -218,10 +218,7 @@ declare namespace T { value: string; } - type CurrentUserSettingNames = - | 'notifications.optOut' - | 'notifications.readDate' - | 'newsbox.dismiss.hotspots'; + type CurrentUserSettingNames = 'notifications.optOut' | 'notifications.readDate'; export interface CustomMeasure { createdAt?: string; diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 9b6c2849812..66657bf4ba6 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -798,9 +798,6 @@ issues.my_issues=My 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. - #------------------------------------------------------------------------------ # -- 2.39.5