From 50fa615acd453be581adda89e7fec784d3adcc34 Mon Sep 17 00:00:00 2001 From: Revanshu Paliwal Date: Tue, 22 Feb 2022 09:29:32 +0100 Subject: [PATCH] SONAR-16007 Binding secondary location click in snippet and hotspot box --- .../conciseIssuesList/ConciseIssueBox.tsx | 1 - .../ConciseIssueBox-test.tsx.snap | 2 - .../security-hotspots/SecurityHotspotsApp.tsx | 30 +++- .../SecurityHotspotsAppRenderer.tsx | 11 ++ .../__tests__/SecurityHotspotsApp-test.tsx | 41 ++++++ .../SecurityHotspotsAppRenderer-test.tsx | 2 + .../SecurityHotspotsApp-test.tsx.snap | 2 + .../SecurityHotspotsAppRenderer-test.tsx.snap | 6 + .../components/HotspotCategory.tsx | 15 +- .../components/HotspotList.tsx | 13 ++ .../components/HotspotListItem.tsx | 16 +- .../components/HotspotSimpleList.tsx | 7 + .../components/HotspotSnippetContainer.tsx | 12 +- .../HotspotSnippetContainerRenderer.tsx | 16 +- .../components/HotspotViewer.tsx | 6 +- .../components/HotspotViewerRenderer.tsx | 8 +- .../components/HotspotViewerTabs.tsx | 9 ++ .../__tests__/HotspotCategory-test.tsx | 2 + .../components/__tests__/HotspotList-test.tsx | 15 ++ .../__tests__/HotspotListItem-test.tsx | 9 +- .../__tests__/HotspotSimpleList-test.tsx | 2 + .../HotspotSnippetContainer-test.tsx | 19 +++ .../HotspotSnippetContainerRenderer-test.tsx | 9 ++ .../__tests__/HotspotViewer-test.tsx | 1 + .../__tests__/HotspotViewerRenderer-test.tsx | 1 + .../__tests__/HotspotViewerTabs-test.tsx | 17 +++ .../HotspotCategory-test.tsx.snap | 12 ++ .../__snapshots__/HotspotList-test.tsx.snap | 16 ++ .../HotspotListItem-test.tsx.snap | 2 +- .../HotspotSimpleList-test.tsx.snap | 16 ++ .../HotspotSnippetContainer-test.tsx.snap | 2 + ...spotSnippetContainerRenderer-test.tsx.snap | 139 +++++++++++++++++- .../__snapshots__/HotspotViewer-test.tsx.snap | 2 + .../HotspotViewerRenderer-test.tsx.snap | 6 + .../js/components/locations/LocationsList.tsx | 1 - .../__tests__/LocationsList-test.tsx | 1 - 36 files changed, 444 insertions(+), 25 deletions(-) diff --git a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueBox.tsx b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueBox.tsx index 2411146ab9c..4cadb1885df 100644 --- a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueBox.tsx +++ b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/ConciseIssueBox.tsx @@ -118,7 +118,6 @@ export default class ConciseIssueBox extends React.PureComponent { isCrossFile={isCrossFile} onLocationSelect={this.props.onLocationSelect} scroll={this.props.scroll} - selectedFlowIndex={selectedFlowIndex} selectedLocationIndex={selectedLocationIndex} /> )} diff --git a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueBox-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueBox-test.tsx.snap index 5badb12ee28..a3239afee18 100644 --- a/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueBox-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/issues/conciseIssuesList/__tests__/__snapshots__/ConciseIssueBox-test.tsx.snap @@ -60,7 +60,6 @@ exports[`should render correctly 1`] = ` locations={Array []} onLocationSelect={[MockFunction]} scroll={[MockFunction]} - selectedFlowIndex={0} selectedLocationIndex={0} uniqueKey="AVsae-CQS-9G3txfbFN2" /> @@ -226,7 +225,6 @@ exports[`should render correctly 2`] = ` } onLocationSelect={[MockFunction]} scroll={[MockFunction]} - selectedFlowIndex={0} selectedLocationIndex={0} uniqueKey="AVsae-CQS-9G3txfbFN2" /> diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsApp.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsApp.tsx index f90fc16bf4b..975dd3c674f 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsApp.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsApp.tsx @@ -28,6 +28,7 @@ import { withCurrentUser } from '../../components/hoc/withCurrentUser'; import { Router } from '../../components/hoc/withRouter'; import { getLeakValue } from '../../components/measure/utils'; import { getBranchLikeQuery, isPullRequest, isSameBranchLike } from '../../helpers/branch-like'; +import { scrollToElement } from '../../helpers/scrolling'; import { getStandards } from '../../helpers/security-standard'; import { isLoggedIn } from '../../helpers/users'; import { fetchBranchStatus } from '../../store/rootActions'; @@ -75,7 +76,7 @@ interface State { loadingMeasure: boolean; loadingMore: boolean; selectedHotspot?: RawHotspot; - selectedHotspotLocation?: number; + selectedHotspotLocationIndex?: number; standards: Standards; } @@ -353,7 +354,8 @@ export class SecurityHotspotsApp extends React.PureComponent { this.handleChangeFilters({ status }); }; - handleHotspotClick = (selectedHotspot: RawHotspot) => this.setState({ selectedHotspot }); + handleHotspotClick = (selectedHotspot: RawHotspot) => + this.setState({ selectedHotspot, selectedHotspotLocationIndex: undefined }); handleHotspotUpdate = (hotspotKey: string) => { const { hotspots, hotspotsPageIndex } = this.state; @@ -419,6 +421,26 @@ export class SecurityHotspotsApp extends React.PureComponent { .catch(this.handleCallFailure); }; + handleLocationClick = (locationIndex: number) => { + const { selectedHotspotLocationIndex } = this.state; + if (locationIndex === selectedHotspotLocationIndex) { + this.setState({ + selectedHotspotLocationIndex: undefined + }); + } else { + this.setState({ + selectedHotspotLocationIndex: locationIndex + }); + } + }; + + handleScroll = (element: Element, bottomOffset = 100) => { + const scrollableElement = document.querySelector('.layout-page-side'); + if (element && scrollableElement) { + scrollToElement(element, { topOffset: 150, bottomOffset, parent: scrollableElement }); + } + }; + render() { const { branchLike, component } = this.props; const { @@ -434,6 +456,7 @@ export class SecurityHotspotsApp extends React.PureComponent { loadingMeasure, loadingMore, selectedHotspot, + selectedHotspotLocationIndex, standards } = this.state; @@ -460,8 +483,11 @@ export class SecurityHotspotsApp extends React.PureComponent { onShowAllHotspots={this.handleShowAllHotspots} onSwitchStatusFilter={this.handleChangeStatusFilter} onUpdateHotspot={this.handleHotspotUpdate} + onLocationClick={this.handleLocationClick} + onScroll={this.handleScroll} securityCategories={standards[SecurityStandard.SONARSOURCE]} selectedHotspot={selectedHotspot} + selectedHotspotLocation={selectedHotspotLocationIndex} standards={standards} /> ); diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsAppRenderer.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsAppRenderer.tsx index deaf72093cd..4de97e96492 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsAppRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/SecurityHotspotsAppRenderer.tsx @@ -56,10 +56,12 @@ export interface SecurityHotspotsAppRendererProps { loadingMore: boolean; onChangeFilters: (filters: Partial) => void; onHotspotClick: (hotspot: RawHotspot) => void; + onLocationClick: (index: number) => void; onLoadMore: () => void; onShowAllHotspots: () => void; onSwitchStatusFilter: (option: HotspotStatusFilter) => void; onUpdateHotspot: (hotspotKey: string) => Promise; + onScroll: (element: Element) => void; selectedHotspot?: RawHotspot; selectedHotspotLocation?: number; securityCategories: StandardSecurityCategories; @@ -83,6 +85,7 @@ export default function SecurityHotspotsAppRenderer(props: SecurityHotspotsAppRe loadingMore, securityCategories, selectedHotspot, + selectedHotspotLocation, standards } = props; @@ -149,6 +152,9 @@ export default function SecurityHotspotsAppRenderer(props: SecurityHotspotsAppRe loadingMore={loadingMore} onHotspotClick={props.onHotspotClick} onLoadMore={props.onLoadMore} + onLocationClick={props.onLocationClick} + onScroll={props.onScroll} + selectedHotspotLocation={selectedHotspotLocation} selectedHotspot={selectedHotspot} standards={standards} /> @@ -160,9 +166,12 @@ export default function SecurityHotspotsAppRenderer(props: SecurityHotspotsAppRe loadingMore={loadingMore} onHotspotClick={props.onHotspotClick} onLoadMore={props.onLoadMore} + onLocationClick={props.onLocationClick} securityCategories={securityCategories} selectedHotspot={selectedHotspot} + selectedHotspotLocation={selectedHotspotLocation} statusFilter={filters.status} + onScroll={props.onScroll} /> )} @@ -177,6 +186,8 @@ export default function SecurityHotspotsAppRenderer(props: SecurityHotspotsAppRe hotspotsReviewedMeasure={hotspotsReviewedMeasure} onSwitchStatusFilter={props.onSwitchStatusFilter} onUpdateHotspot={props.onUpdateHotspot} + onLocationClick={props.onLocationClick} + selectedHotspotLocation={selectedHotspotLocation} /> diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsApp-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsApp-test.tsx index 5da406c5330..ed73077f227 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsApp-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsApp-test.tsx @@ -19,6 +19,7 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; +import { mockHtmlElement } from '../../../helpers/mocks/dom'; import { getMeasures } from '../../../api/measures'; import { getSecurityHotspotList, getSecurityHotspots } from '../../../api/security-hotspots'; import { mockBranch, mockPullRequest } from '../../../helpers/mocks/branch-like'; @@ -40,6 +41,7 @@ import { } from '../../../types/security-hotspots'; import { SecurityHotspotsApp } from '../SecurityHotspotsApp'; import SecurityHotspotsAppRenderer from '../SecurityHotspotsAppRenderer'; +import { scrollToElement } from '../../../helpers/scrolling'; beforeEach(() => jest.clearAllMocks()); @@ -56,6 +58,10 @@ jest.mock('../../../helpers/security-standard', () => ({ getStandards: jest.fn().mockResolvedValue({ sonarsourceSecurity: { cat1: { title: 'cat 1' } } }) })); +jest.mock('../../../helpers/scrolling', () => ({ + scrollToElement: jest.fn() +})); + const branch = mockBranch(); it('should render correctly', () => { @@ -379,6 +385,41 @@ it('should handle leakPeriod filter change', async () => { expect(getSecurityHotspots).toBeCalledWith(expect.objectContaining({ sinceLeakPeriod: true })); }); +it('should handle hotspot click', () => { + const wrapper = shallowRender(); + const selectedHotspot = mockRawHotspot(); + wrapper.instance().handleHotspotClick(selectedHotspot); + + expect(wrapper.instance().state.selectedHotspotLocationIndex).toBeUndefined(); + expect(wrapper.instance().state.selectedHotspot).toEqual(selectedHotspot); +}); + +it('should handle secondary location click', () => { + const wrapper = shallowRender(); + wrapper.instance().handleLocationClick(0); + expect(wrapper.instance().state.selectedHotspotLocationIndex).toEqual(0); + + wrapper.instance().handleLocationClick(1); + expect(wrapper.instance().state.selectedHotspotLocationIndex).toEqual(1); + + wrapper.instance().handleLocationClick(1); + expect(wrapper.instance().state.selectedHotspotLocationIndex).toBeUndefined(); +}); + +it('should handle scroll properly', async () => { + const fakeElement = document.createElement('div'); + jest.spyOn(document, 'querySelector').mockImplementationOnce(() => fakeElement); + const wrapper = shallowRender(); + const element = mockHtmlElement(); + wrapper.instance().handleScroll(element); + await waitAndUpdate(wrapper); + expect(scrollToElement).toBeCalledWith(element, { + bottomOffset: 100, + parent: fakeElement, + topOffset: 150 + }); +}); + describe('keyboard navigation', () => { const hotspots = [ mockRawHotspot({ key: 'k1' }), diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsAppRenderer-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsAppRenderer-test.tsx index 577ed513560..6f1b477c1c1 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsAppRenderer-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/SecurityHotspotsAppRenderer-test.tsx @@ -146,6 +146,8 @@ function shallowRender(props: Partial = {}) { onShowAllHotspots={jest.fn()} onSwitchStatusFilter={jest.fn()} onUpdateHotspot={jest.fn()} + onLocationClick={jest.fn()} + onScroll={jest.fn()} securityCategories={{}} selectedHotspot={undefined} standards={mockStandards()} diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsApp-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsApp-test.tsx.snap index 4775fd8a595..1762e1a557e 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsApp-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsApp-test.tsx.snap @@ -48,6 +48,8 @@ exports[`should render correctly 1`] = ` onChangeFilters={[Function]} onHotspotClick={[Function]} onLoadMore={[Function]} + onLocationClick={[Function]} + onScroll={[Function]} onShowAllHotspots={[Function]} onSwitchStatusFilter={[Function]} onUpdateHotspot={[Function]} diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsAppRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsAppRenderer-test.tsx.snap index ce093d196af..bd4730c4110 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsAppRenderer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/security-hotspots/__tests__/__snapshots__/SecurityHotspotsAppRenderer-test.tsx.snap @@ -117,6 +117,8 @@ exports[`should render correctly when filtered by category or cwe: category 1`] loadingMore={false} onHotspotClick={[MockFunction]} onLoadMore={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} selectedHotspot={ Object { "author": "Developer 1", @@ -236,6 +238,8 @@ exports[`should render correctly when filtered by category or cwe: cwe 1`] = ` loadingMore={false} onHotspotClick={[MockFunction]} onLoadMore={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} selectedHotspot={ Object { "author": "Developer 1", @@ -415,6 +419,8 @@ exports[`should render correctly with hotspots 2`] = ` loadingMore={false} onHotspotClick={[MockFunction]} onLoadMore={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} securityCategories={Object {}} selectedHotspot={ Object { diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotCategory.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotCategory.tsx index fa176838b89..5365ef9d372 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotCategory.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotCategory.tsx @@ -30,6 +30,8 @@ export interface HotspotCategoryProps { hotspots: RawHotspot[]; onHotspotClick: (hotspot: RawHotspot) => void; onToggleExpand?: (categoryKey: string, value: boolean) => void; + onLocationClick: (index: number) => void; + onScroll: (element: Element) => void; selectedHotspot: RawHotspot; selectedHotspotLocation?: number; title: string; @@ -37,7 +39,15 @@ export interface HotspotCategoryProps { } export default function HotspotCategory(props: HotspotCategoryProps) { - const { categoryKey, expanded, hotspots, selectedHotspot, title, isLastAndIncomplete } = props; + const { + categoryKey, + expanded, + hotspots, + selectedHotspot, + title, + isLastAndIncomplete, + selectedHotspotLocation + } = props; if (hotspots.length < 1) { return null; @@ -80,6 +90,9 @@ export default function HotspotCategory(props: HotspotCategoryProps) { diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotList.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotList.tsx index afa5194f180..610aa769544 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotList.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotList.tsx @@ -37,6 +37,8 @@ interface Props { loadingMore: boolean; onHotspotClick: (hotspot: RawHotspot) => void; onLoadMore: () => void; + onLocationClick: (index: number) => void; + onScroll: (element: Element) => void; securityCategories: StandardSecurityCategories; selectedHotspot: RawHotspot; selectedHotspotLocation?: number; @@ -84,6 +86,13 @@ export default class HotspotList extends React.Component { ); this.setState({ groupedHotspots }); } + + if (this.props.selectedHotspotLocation !== prevProps.selectedHotspotLocation) { + const { selectedHotspot } = this.props; + this.setState({ + expandedCategories: { [selectedHotspot.securityCategory]: true } + }); + } } componentWillUnmount() { @@ -112,6 +121,7 @@ export default class HotspotList extends React.Component { isStaticListOfHotspots, loadingMore, selectedHotspot, + selectedHotspotLocation, statusFilter } = this.props; @@ -150,7 +160,10 @@ export default class HotspotList extends React.Component { hotspots={cat.hotspots} onHotspotClick={this.props.onHotspotClick} onToggleExpand={this.handleToggleCategory} + onLocationClick={this.props.onLocationClick} + onScroll={this.props.onScroll} selectedHotspot={selectedHotspot} + selectedHotspotLocation={selectedHotspotLocation} title={cat.title} isLastAndIncomplete={ isLastRiskGroup && isLastCategory && hotspots.length < hotspotsTotal diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotListItem.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotListItem.tsx index 7865080337c..e2e34da722e 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotListItem.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotListItem.tsx @@ -22,17 +22,20 @@ import * as React from 'react'; import QualifierIcon from '../../../components/icons/QualifierIcon'; import { ComponentQualifier } from '../../../types/component'; import { getFilePath, getLocations } from '../utils'; -import LocationsList from '../../../components/locations/LocationsList'; import { RawHotspot } from '../../../types/security-hotspots'; +import LocationsList from '../../../components/locations/LocationsList'; export interface HotspotListItemProps { hotspot: RawHotspot; onClick: (hotspot: RawHotspot) => void; + onLocationClick: (index: number) => void; + onScroll: (element: Element) => void; selected: boolean; + selectedHotspotLocation?: number; } export default function HotspotListItem(props: HotspotListItemProps) { - const { hotspot, selected } = props; + const { hotspot, selected, selectedHotspotLocation } = props; const locations = getLocations(hotspot.flows, undefined); const path = getFilePath(hotspot.component, hotspot.project); @@ -56,12 +59,9 @@ export default function HotspotListItem(props: HotspotListItemProps) { locations={locations} isCrossFile={false} // Currently we are not supporting cross file for security hotspot uniqueKey={hotspot.key} - onLocationSelect={() => { - /* noop */ - }} - scroll={() => { - /* noop */ - }} + onLocationSelect={props.onLocationClick} + selectedLocationIndex={selectedHotspotLocation} + scroll={props.onScroll} /> )} diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSimpleList.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSimpleList.tsx index 11a96cbccc4..ca7c81cd7a3 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSimpleList.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSimpleList.tsx @@ -42,8 +42,11 @@ export interface HotspotSimpleListProps { hotspotsTotal: number; loadingMore: boolean; onHotspotClick: (hotspot: RawHotspot) => void; + onLocationClick: (index: number) => void; + onScroll: (element: Element) => void; onLoadMore: () => void; selectedHotspot: RawHotspot; + selectedHotspotLocation?: number; standards: Standards; } @@ -65,6 +68,7 @@ export default class HotspotSimpleList extends React.Component ))} diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainer.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainer.tsx index 690f1047d1c..f858ee711e4 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainer.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainer.tsx @@ -18,6 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; +import { scrollToElement } from '../../../helpers/scrolling'; import { getSources } from '../../../api/components'; import { locationsByLine } from '../../../components/SourceViewer/helpers/indexing'; import { getBranchLikeQuery } from '../../../helpers/branch-like'; @@ -32,6 +33,8 @@ interface Props { component: Component; hotspot: Hotspot; onCommentButtonClick: () => void; + onLocationSelect: (index: number) => void; + selectedHotspotLocation?: number; } interface State { @@ -195,8 +198,12 @@ export default class HotspotSnippetContainer extends React.Component { + scrollToElement(element, { topOffset: offset - 100, bottomOffset: offset, smooth }); + }; + render() { - const { branchLike, component, hotspot } = this.props; + const { branchLike, component, hotspot, selectedHotspotLocation } = this.props; const { highlightedSymbols, lastLine, loading, sourceLines, secondaryLocations } = this.state; const locations = locationsByLine([hotspot]); @@ -214,9 +221,12 @@ export default class HotspotSnippetContainer extends React.Component ); } diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainerRenderer.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainerRenderer.tsx index 80998643a2f..459c1610fe1 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainerRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainerRenderer.tsx @@ -40,9 +40,12 @@ export interface HotspotSnippetContainerRendererProps { hotspot: Hotspot; loading: boolean; locations: { [line: number]: LinearIssueLocation[] }; + selectedHotspotLocation?: number; onCommentButtonClick: () => void; onExpandBlock: (direction: ExpandDirection) => Promise; onSymbolClick: (symbols: string[]) => void; + onLocationSelect: (index: number) => void; + onScroll: (element: Element) => void; sourceLines: SourceLine[]; sourceViewerFile: SourceViewerFile; secondaryLocations: FlowLocation[]; @@ -63,7 +66,8 @@ export default function HotspotSnippetContainerRenderer( sourceLines, sourceViewerFile, secondaryLocations, - component + component, + selectedHotspotLocation } = props; const renderHotspotBoxInLine = (lineNumber: number) => @@ -73,6 +77,11 @@ export default function HotspotSnippetContainerRenderer( undefined ); + const highlightedLocation = + selectedHotspotLocation !== undefined + ? { index: selectedHotspotLocation, text: hotspot.message } + : undefined; + return ( <> {!loading && sourceLines.length === 0 && ( @@ -91,7 +100,7 @@ export default function HotspotSnippetContainerRenderer( handleCloseIssues={noop} handleOpenIssues={noop} handleSymbolClick={props.onSymbolClick} - highlightedLocationMessage={undefined} + highlightedLocationMessage={highlightedLocation} highlightedSymbols={highlightedSymbols} index={0} issue={hotspot} @@ -101,11 +110,12 @@ export default function HotspotSnippetContainerRenderer( locationsByLine={primaryLocations} onIssueChange={noop} onIssuePopupToggle={noop} - onLocationSelect={noop} + onLocationSelect={props.onLocationSelect} openIssuesByLine={{}} renderAdditionalChildInLine={renderHotspotBoxInLine} renderDuplicationPopup={noop} snippet={sourceLines} + scroll={props.onScroll} /> )} diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewer.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewer.tsx index 860c97f2bbb..14c648765fd 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewer.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewer.tsx @@ -35,6 +35,8 @@ interface Props { hotspotsReviewedMeasure?: string; onSwitchStatusFilter: (option: HotspotStatusFilter) => void; onUpdateHotspot: (hotspotKey: string) => Promise; + onLocationClick: (index: number) => void; + selectedHotspotLocation?: number; } interface State { @@ -116,7 +118,7 @@ export default class HotspotViewer extends React.PureComponent { }; render() { - const { component, hotspotsReviewedMeasure } = this.props; + const { component, hotspotsReviewedMeasure, selectedHotspotLocation } = this.props; const { hotspot, lastStatusChangedTo, loading, showStatusUpdateSuccessModal } = this.state; return ( @@ -131,7 +133,9 @@ export default class HotspotViewer extends React.PureComponent { onSwitchFilterToStatusOfUpdatedHotspot={this.handleSwitchFilterToStatusOfUpdatedHotspot} onShowCommentForm={this.handleScrollToCommentForm} onUpdateHotspot={this.handleHotspotUpdate} + onLocationClick={this.props.onLocationClick} showStatusUpdateSuccessModal={showStatusUpdateSuccessModal} + selectedHotspotLocation={selectedHotspotLocation} /> ); } diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerRenderer.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerRenderer.tsx index a46ab94816b..0fc2f8540b0 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerRenderer.tsx @@ -42,7 +42,9 @@ export interface HotspotViewerRendererProps { onUpdateHotspot: (statusUpdate?: boolean, statusOption?: HotspotStatusOption) => Promise; onShowCommentForm: () => void; onSwitchFilterToStatusOfUpdatedHotspot: () => void; + onLocationClick: (index: number) => void; showStatusUpdateSuccessModal: boolean; + selectedHotspotLocation?: number; } export function HotspotViewerRenderer(props: HotspotViewerRendererProps) { @@ -54,7 +56,8 @@ export function HotspotViewerRenderer(props: HotspotViewerRendererProps) { loading, lastStatusChangedTo, showStatusUpdateSuccessModal, - commentTextRef + commentTextRef, + selectedHotspotLocation } = props; return ( @@ -78,9 +81,12 @@ export function HotspotViewerRenderer(props: HotspotViewerRendererProps) { component={component} hotspot={hotspot} onCommentButtonClick={props.onShowCommentForm} + onLocationSelect={props.onLocationClick} + selectedHotspotLocation={selectedHotspotLocation} /> } hotspot={hotspot} + selectedHotspotLocation={selectedHotspotLocation} /> currentTab: tabs[0], tabs }); + } else if ( + this.props.selectedHotspotLocation !== undefined && + this.props.selectedHotspotLocation !== prevProps.selectedHotspotLocation + ) { + const { tabs } = this.state; + this.setState({ + currentTab: tabs[0] + }); } } diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotCategory-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotCategory-test.tsx index cc1f787a8d4..55f7f5c6dc0 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotCategory-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotCategory-test.tsx @@ -75,6 +75,8 @@ function shallowRender(props: Partial = {}) { selectedHotspot={mockRawHotspot()} title="Class Injection" isLastAndIncomplete={false} + onLocationClick={jest.fn()} + onScroll={jest.fn()} {...props} /> ); diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotList-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotList-test.tsx index 4d21f225d7a..2d9aa8ea056 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotList-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotList-test.tsx @@ -98,6 +98,19 @@ it('should update grouped hotspots when the list changes', () => { expect(wrapper.state().groupedHotspots[0].categories[0].hotspots).toHaveLength(1); }); +it('should expand the categories for which the location is selected', () => { + const wrapper = shallowRender({ hotspots, selectedHotspot: hotspots[0] }); + + expect(wrapper.state().expandedCategories).toEqual({ cat2: true }); + + wrapper.instance().handleToggleCategory('cat2', false); + expect(wrapper.state().expandedCategories).toEqual({ cat2: false }); + + wrapper.setProps({ selectedHotspotLocation: 1 }); + + expect(wrapper.state().expandedCategories).toEqual({ cat2: true }); +}); + function shallowRender(props: Partial = {}) { return shallow( = {}) { loadingMore={false} onHotspotClick={jest.fn()} onLoadMore={jest.fn()} + onLocationClick={jest.fn()} + onScroll={jest.fn()} securityCategories={{}} selectedHotspot={mockRawHotspot({ key: 'h2' })} statusFilter={HotspotStatusFilter.TO_REVIEW} diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotListItem-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotListItem-test.tsx index e5090040fac..09187d1f218 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotListItem-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotListItem-test.tsx @@ -39,6 +39,13 @@ it('should handle click', () => { function shallowRender(props: Partial = {}) { return shallow( - + ); } diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSimpleList-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSimpleList-test.tsx index 525156472ae..a74de8f1277 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSimpleList-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSimpleList-test.tsx @@ -64,6 +64,8 @@ function shallowRender(props: Partial = {}) { loadingMore={false} onHotspotClick={jest.fn()} onLoadMore={jest.fn()} + onLocationClick={jest.fn()} + onScroll={jest.fn()} selectedHotspot={hotspots[0]} standards={{ cwe: { 327: { title: 'Use of a Broken or Risky Cryptographic Algorithm' } }, diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainer-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainer-test.tsx index 98edc5eddf2..8a0fb0e734d 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainer-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainer-test.tsx @@ -20,6 +20,8 @@ import { shallow } from 'enzyme'; import { range } from 'lodash'; import * as React from 'react'; +import { mockHtmlElement } from '../../../../helpers/mocks/dom'; +import { scrollToElement } from '../../../../helpers/scrolling'; import { getSources } from '../../../../api/components'; import { mockBranch } from '../../../../helpers/mocks/branch-like'; import { mockComponent } from '../../../../helpers/mocks/component'; @@ -34,6 +36,10 @@ jest.mock('../../../../api/components', () => ({ getSources: jest.fn().mockResolvedValue([]) })); +jest.mock('../../../../helpers/scrolling', () => ({ + scrollToElement: jest.fn() +})); + beforeEach(() => jest.clearAllMocks()); const branch = mockBranch(); @@ -214,6 +220,18 @@ it('should handle symbol click', () => { expect(wrapper.state().highlightedSymbols).toBe(symbols); }); +it('should handle scroll properly', async () => { + const wrapper = shallowRender(); + const element = mockHtmlElement(); + wrapper.instance().handleScroll(element, 200); + await waitAndUpdate(wrapper); + expect(scrollToElement).toBeCalledWith(element, { + bottomOffset: 200, + smooth: true, + topOffset: 100 + }); +}); + function shallowRender(props?: Partial) { return shallow( ) { component={mockComponent()} hotspot={mockHotspot()} onCommentButtonClick={jest.fn()} + onLocationSelect={jest.fn()} {...props} /> ); diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainerRenderer-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainerRenderer-test.tsx index 2b36ee19b29..5afde638a1a 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainerRenderer-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainerRenderer-test.tsx @@ -45,6 +45,13 @@ it('should render a HotspotPrimaryLocationBox', () => { expect(renderAdditionalChildInLine!(42)).not.toBeUndefined(); }); +it('should render correctly when secondary location is selected', () => { + const wrapper = shallowRender({ + selectedHotspotLocation: 1 + }); + expect(wrapper).toMatchSnapshot('with selected hotspot location'); +}); + function shallowRender(props?: Partial) { return shallow( ) { sourceLines={[]} sourceViewerFile={mockSourceViewerFile()} component={mockComponent()} + onLocationSelect={jest.fn()} + onScroll={jest.fn()} {...props} /> ); diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewer-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewer-test.tsx index 31a07d26364..264e81a5812 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewer-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewer-test.tsx @@ -143,6 +143,7 @@ function shallowRender(props?: Partial) { hotspotKey={hotspotKey} onSwitchStatusFilter={jest.fn()} onUpdateHotspot={jest.fn()} + onLocationClick={jest.fn()} {...props} /> ); diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewerRenderer-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewerRenderer-test.tsx index d99c07fc360..391389ef308 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewerRenderer-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewerRenderer-test.tsx @@ -64,6 +64,7 @@ function shallowRender(props?: Partial) { onShowCommentForm={jest.fn()} onUpdateHotspot={jest.fn()} showStatusUpdateSuccessModal={false} + onLocationClick={jest.fn()} {...props} /> ); diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewerTabs-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewerTabs-test.tsx index 941b24e0364..2ba96c577f8 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewerTabs-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewerTabs-test.tsx @@ -96,6 +96,23 @@ it('should select first tab on hotspot update', () => { expect(wrapper.state().currentTab.key).toBe(TabKeys.Code); }); +it('should select first tab when hotspot location is selected and is not undefined', () => { + const wrapper = shallowRender(); + const onSelect: (tab: TabKeys) => void = wrapper.find(BoxedTabs).prop('onSelect'); + + onSelect(TabKeys.VulnerabilityDescription); + expect(wrapper.state().currentTab.key).toBe(TabKeys.VulnerabilityDescription); + + wrapper.setProps({ selectedHotspotLocation: 1 }); + expect(wrapper.state().currentTab.key).toBe(TabKeys.Code); + + onSelect(TabKeys.VulnerabilityDescription); + expect(wrapper.state().currentTab.key).toBe(TabKeys.VulnerabilityDescription); + + wrapper.setProps({ selectedHotspotLocation: undefined }); + expect(wrapper.state().currentTab.key).toBe(TabKeys.VulnerabilityDescription); +}); + function shallowRender(props?: Partial) { return shallow( @@ -75,6 +77,8 @@ exports[`should render correctly with hotspots 1`] = ` } } onClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} selected={false} /> @@ -159,6 +163,8 @@ exports[`should render correctly with hotspots: contains selected 1`] = ` } } onClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} selected={true} /> @@ -185,6 +191,8 @@ exports[`should render correctly with hotspots: contains selected 1`] = ` } } onClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} selected={false} /> @@ -242,6 +250,8 @@ exports[`should render correctly with hotspots: lastAndIncomplete 1`] = ` } } onClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} selected={false} /> @@ -268,6 +278,8 @@ exports[`should render correctly with hotspots: lastAndIncomplete 1`] = ` } } onClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} selected={false} /> diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotList-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotList-test.tsx.snap index f506600e1ad..9fd195681f6 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotList-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotList-test.tsx.snap @@ -131,6 +131,8 @@ exports[`should render correctly with hotspots: no pagination 1`] = ` } isLastAndIncomplete={false} onHotspotClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} onToggleExpand={[Function]} selectedHotspot={ Object { @@ -179,6 +181,8 @@ exports[`should render correctly with hotspots: no pagination 1`] = ` } isLastAndIncomplete={false} onHotspotClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} onToggleExpand={[Function]} selectedHotspot={ Object { @@ -262,6 +266,8 @@ exports[`should render correctly with hotspots: no pagination 1`] = ` } isLastAndIncomplete={false} onHotspotClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} onToggleExpand={[Function]} selectedHotspot={ Object { @@ -310,6 +316,8 @@ exports[`should render correctly with hotspots: no pagination 1`] = ` } isLastAndIncomplete={false} onHotspotClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} onToggleExpand={[Function]} selectedHotspot={ Object { @@ -403,6 +411,8 @@ exports[`should render correctly with hotspots: pagination 1`] = ` } isLastAndIncomplete={false} onHotspotClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} onToggleExpand={[Function]} selectedHotspot={ Object { @@ -451,6 +461,8 @@ exports[`should render correctly with hotspots: pagination 1`] = ` } isLastAndIncomplete={false} onHotspotClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} onToggleExpand={[Function]} selectedHotspot={ Object { @@ -534,6 +546,8 @@ exports[`should render correctly with hotspots: pagination 1`] = ` } isLastAndIncomplete={false} onHotspotClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} onToggleExpand={[Function]} selectedHotspot={ Object { @@ -582,6 +596,8 @@ exports[`should render correctly with hotspots: pagination 1`] = ` } isLastAndIncomplete={true} onHotspotClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} onToggleExpand={[Function]} selectedHotspot={ Object { diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotListItem-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotListItem-test.tsx.snap index 0d0f163550e..41a9dbae849 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotListItem-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotListItem-test.tsx.snap @@ -61,7 +61,7 @@ exports[`should render correctly 2`] = ` isCrossFile={false} locations={Array []} onLocationSelect={[Function]} - scroll={[Function]} + scroll={[MockFunction]} uniqueKey="01fc972e-2a3c-433e-bcae-0bd7f88f5123" /> diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSimpleList-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSimpleList-test.tsx.snap index 950f99e0f46..b7e685bd3f0 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSimpleList-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSimpleList-test.tsx.snap @@ -53,6 +53,8 @@ exports[`should render correctly: filter by both 1`] = ` } } onClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} selected={true} /> @@ -79,6 +81,8 @@ exports[`should render correctly: filter by both 1`] = ` } } onClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} selected={false} /> @@ -145,6 +149,8 @@ exports[`should render correctly: filter by category 1`] = ` } } onClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} selected={true} /> @@ -171,6 +177,8 @@ exports[`should render correctly: filter by category 1`] = ` } } onClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} selected={false} /> @@ -237,6 +245,8 @@ exports[`should render correctly: filter by cwe 1`] = ` } } onClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} selected={true} /> @@ -263,6 +273,8 @@ exports[`should render correctly: filter by cwe 1`] = ` } } onClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} selected={false} /> @@ -339,6 +351,8 @@ exports[`should render correctly: filter by file 1`] = ` } } onClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} selected={true} /> @@ -365,6 +379,8 @@ exports[`should render correctly: filter by file 1`] = ` } } onClick={[MockFunction]} + onLocationClick={[MockFunction]} + onScroll={[MockFunction]} selected={false} /> diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSnippetContainer-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSnippetContainer-test.tsx.snap index eae97ea5a08..bf4192ecb35 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSnippetContainer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSnippetContainer-test.tsx.snap @@ -133,6 +133,8 @@ exports[`should render correctly 1`] = ` } onCommentButtonClick={[MockFunction]} onExpandBlock={[Function]} + onLocationSelect={[MockFunction]} + onScroll={[Function]} onSymbolClick={[Function]} secondaryLocations={ Array [ diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSnippetContainerRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSnippetContainerRenderer-test.tsx.snap index 5a9f5912a14..71d62cac55b 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSnippetContainerRenderer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSnippetContainerRenderer-test.tsx.snap @@ -136,6 +136,142 @@ exports[`should render correctly 1`] = ` `; +exports[`should render correctly when secondary location is selected: with selected hotspot location 1`] = ` + +

+ hotspots.no_associated_lines +

+ This a strong message about fixing !

", + "key": "squid:S2077", + "name": "That rule", + "riskDescription": "

This a strong message about risk !

", + "securityCategory": "sql-injection", + "vulnerabilityDescription": "

This a strong message about vulnerability !

", + "vulnerabilityProbability": "HIGH", + }, + "status": "REVIEWED", + "textRange": Object { + "endLine": 142, + "endOffset": 83, + "startLine": 142, + "startOffset": 26, + }, + "updateDate": "2013-05-13T17:55:42+0200", + "users": Array [ + Object { + "active": true, + "local": true, + "login": "assignee", + "name": "John Doe", + }, + Object { + "active": true, + "local": true, + "login": "author", + "name": "John Doe", + }, + ], + } + } + /> +
+ +
+
+`; + exports[`should render correctly: with sourcelines 1`] = ` } hotspot={ @@ -610,6 +611,7 @@ exports[`should render correctly: assignee without name 1`] = ` } } onCommentButtonClick={[MockFunction]} + onLocationSelect={[MockFunction]} /> } hotspot={ @@ -1011,6 +1013,7 @@ exports[`should render correctly: default 1`] = ` } } onCommentButtonClick={[MockFunction]} + onLocationSelect={[MockFunction]} /> } hotspot={ @@ -1412,6 +1415,7 @@ exports[`should render correctly: deleted assignee 1`] = ` } } onCommentButtonClick={[MockFunction]} + onLocationSelect={[MockFunction]} /> } hotspot={ @@ -1826,6 +1830,7 @@ exports[`should render correctly: show success modal 1`] = ` } } onCommentButtonClick={[MockFunction]} + onLocationSelect={[MockFunction]} /> } hotspot={ @@ -2227,6 +2232,7 @@ exports[`should render correctly: unassigned 1`] = ` } } onCommentButtonClick={[MockFunction]} + onLocationSelect={[MockFunction]} /> } hotspot={ diff --git a/server/sonar-web/src/main/js/components/locations/LocationsList.tsx b/server/sonar-web/src/main/js/components/locations/LocationsList.tsx index 6a38b047a05..19e52f004c0 100644 --- a/server/sonar-web/src/main/js/components/locations/LocationsList.tsx +++ b/server/sonar-web/src/main/js/components/locations/LocationsList.tsx @@ -28,7 +28,6 @@ interface Props { locations: FlowLocation[]; onLocationSelect: (index: number) => void; scroll: (element: Element) => void; - selectedFlowIndex?: number; selectedLocationIndex?: number; } diff --git a/server/sonar-web/src/main/js/components/locations/__tests__/LocationsList-test.tsx b/server/sonar-web/src/main/js/components/locations/__tests__/LocationsList-test.tsx index 48b9868be13..3f525e8845c 100644 --- a/server/sonar-web/src/main/js/components/locations/__tests__/LocationsList-test.tsx +++ b/server/sonar-web/src/main/js/components/locations/__tests__/LocationsList-test.tsx @@ -67,7 +67,6 @@ function shallowRender(overrides: Partial = {}) { isCrossFile={true} onLocationSelect={jest.fn()} scroll={jest.fn()} - selectedFlowIndex={undefined} selectedLocationIndex={undefined} {...overrides} /> -- 2.39.5