aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web
diff options
context:
space:
mode:
authorPhilippe Perrin <philippe.perrin@sonarsource.com>2022-02-14 17:50:45 +0100
committersonartech <sonartech@sonarsource.com>2022-02-25 20:02:54 +0000
commit0635374eb836f7994bf759fa4473f750c7b5a63b (patch)
treebd6c19d1549a10fbfcaf0cb3f37e6472bb265bb5 /server/sonar-web
parente5474111c8f3e985cfa751d8cd86f1950aaf8e4d (diff)
downloadsonarqube-0635374eb836f7994bf759fa4473f750c7b5a63b.tar.gz
sonarqube-0635374eb836f7994bf759fa4473f750c7b5a63b.zip
SONAR-16007 Display secondary locations within hotspot code snippet
Diffstat (limited to 'server/sonar-web')
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainer.tsx55
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotSnippetContainerRenderer.tsx61
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainer-test.tsx25
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainerRenderer-test.tsx1
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistoryAndComments-test.tsx.snap30
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSnippetContainer-test.tsx.snap30
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSnippetContainerRenderer-test.tsx.snap254
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotViewerRenderer-test.tsx.snap450
-rw-r--r--server/sonar-web/src/main/js/apps/security-hotspots/components/status/__tests__/__snapshots__/Status-test.tsx.snap45
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/LineCode.tsx21
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineCode-test.tsx7
-rw-r--r--server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineCode-test.tsx.snap136
-rw-r--r--server/sonar-web/src/main/js/helpers/mocks/security-hotspots.ts3
-rw-r--r--server/sonar-web/src/main/js/types/security-hotspots.ts10
14 files changed, 939 insertions, 189 deletions
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 9b8928dc4aa..6318465460f 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
@@ -24,8 +24,8 @@ import { getBranchLikeQuery } from '../../../helpers/branch-like';
import { BranchLike } from '../../../types/branch-like';
import { ComponentQualifier } from '../../../types/component';
import { Hotspot } from '../../../types/security-hotspots';
-import { Component, ExpandDirection, SourceLine } from '../../../types/types';
-import { constructSourceViewerFile } from '../utils';
+import { Component, ExpandDirection, FlowLocation, SourceLine } from '../../../types/types';
+import { constructSourceViewerFile, getLocations } from '../utils';
import HotspotSnippetContainerRenderer from './HotspotSnippetContainerRenderer';
interface Props {
@@ -40,6 +40,7 @@ interface State {
lastLine?: number;
loading: boolean;
sourceLines: SourceLine[];
+ secondaryLocations: FlowLocation[];
}
const BUFFER_LINES = 5;
@@ -50,16 +51,19 @@ export default class HotspotSnippetContainer extends React.Component<Props, Stat
state: State = {
highlightedSymbols: [],
loading: true,
- sourceLines: []
+ sourceLines: [],
+ secondaryLocations: []
};
- componentWillMount() {
+ async componentWillMount() {
this.mounted = true;
+ await this.initializeSecondaryLocations();
this.fetchSources();
}
- componentDidUpdate(prevProps: Props) {
+ async componentDidUpdate(prevProps: Props) {
if (prevProps.hotspot.key !== this.props.hotspot.key) {
+ await this.initializeSecondaryLocations();
this.fetchSources();
}
}
@@ -85,15 +89,30 @@ export default class HotspotSnippetContainer extends React.Component<Props, Stat
hotspot: { component, textRange }
} = this.props;
+ const { secondaryLocations } = this.state;
+
if (!textRange) {
// Hotspot not associated to any loc
this.setState({ loading: false, lastLine: undefined, sourceLines: [] });
return;
}
- const from = Math.max(1, textRange.startLine - BUFFER_LINES);
- // Add 1 to check for end-of-file:
- const to = textRange.endLine + BUFFER_LINES + 1;
+ // Search for the min startLine within primary and secondary locations
+ const from = Math.max(
+ 1,
+ Math.min(
+ ...[textRange, ...secondaryLocations.map(l => l.textRange)].map(
+ t => t.startLine - BUFFER_LINES
+ )
+ )
+ );
+ // Search for the max endLine within primary and secondary locations
+ const to = Math.max(
+ ...[textRange, ...secondaryLocations.map(l => l.textRange)].map(
+ // Add 1 to check for end-of-file
+ t => t.endLine + BUFFER_LINES + 1
+ )
+ );
this.setState({ loading: true });
@@ -113,6 +132,23 @@ export default class HotspotSnippetContainer extends React.Component<Props, Stat
}
}
+ initializeSecondaryLocations() {
+ const { hotspot } = this.props;
+
+ return new Promise(resolve => {
+ this.setState(
+ {
+ secondaryLocations: getLocations(hotspot.flows, undefined).map((location, index) => ({
+ ...location,
+ index,
+ text: location.msg
+ }))
+ },
+ () => resolve(undefined)
+ );
+ });
+ }
+
handleExpansion = (direction: ExpandDirection) => {
const { branchLike, hotspot } = this.props;
const { sourceLines } = this.state;
@@ -162,7 +198,7 @@ export default class HotspotSnippetContainer extends React.Component<Props, Stat
render() {
const { branchLike, component, hotspot } = this.props;
- const { highlightedSymbols, lastLine, loading, sourceLines } = this.state;
+ const { highlightedSymbols, lastLine, loading, sourceLines, secondaryLocations } = this.state;
const locations = locationsByLine([hotspot]);
@@ -181,6 +217,7 @@ export default class HotspotSnippetContainer extends React.Component<Props, Stat
onSymbolClick={this.handleSymbolClick}
sourceLines={sourceLines}
sourceViewerFile={sourceViewerFile}
+ secondaryLocations={secondaryLocations}
/>
);
}
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 15c8ac2a848..dfe5c696eb4 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
@@ -18,7 +18,6 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
-import { SourceViewerContext } from '../../../components/SourceViewer/SourceViewerContext';
import SourceViewerHeaderSlim from '../../../components/SourceViewer/SourceViewerHeaderSlim';
import DeferredSpinner from '../../../components/ui/DeferredSpinner';
import { translate } from '../../../helpers/l10n';
@@ -26,6 +25,7 @@ import { BranchLike } from '../../../types/branch-like';
import { Hotspot } from '../../../types/security-hotspots';
import {
ExpandDirection,
+ FlowLocation,
LinearIssueLocation,
SourceLine,
SourceViewerFile
@@ -45,6 +45,7 @@ export interface HotspotSnippetContainerRendererProps {
onSymbolClick: (symbols: string[]) => void;
sourceLines: SourceLine[];
sourceViewerFile: SourceViewerFile;
+ secondaryLocations: FlowLocation[];
}
const noop = () => undefined;
@@ -58,9 +59,10 @@ export default function HotspotSnippetContainerRenderer(
highlightedSymbols,
hotspot,
loading,
- locations,
+ locations: primaryLocations,
sourceLines,
- sourceViewerFile
+ sourceViewerFile,
+ secondaryLocations
} = props;
const renderHotspotBoxInLine = (lineNumber: number) =>
@@ -87,34 +89,31 @@ export default function HotspotSnippetContainerRenderer(
/>
<DeferredSpinner className="big-spacer" loading={loading}>
{sourceLines.length > 0 && (
- <SourceViewerContext.Provider /* Used by LineOptionsPopup */
- value={{ branchLike, file: sourceViewerFile }}>
- <SnippetViewer
- branchLike={undefined}
- component={sourceViewerFile}
- displayLineNumberOptions={false}
- displaySCM={false}
- expandBlock={(_i, direction) => props.onExpandBlock(direction)}
- handleCloseIssues={noop}
- handleOpenIssues={noop}
- handleSymbolClick={props.onSymbolClick}
- highlightedLocationMessage={undefined}
- highlightedSymbols={highlightedSymbols}
- index={0}
- issue={hotspot}
- issuesByLine={{}}
- lastSnippetOfLastGroup={false}
- locations={[]}
- locationsByLine={locations}
- onIssueChange={noop}
- onIssuePopupToggle={noop}
- onLocationSelect={noop}
- openIssuesByLine={{}}
- renderAdditionalChildInLine={renderHotspotBoxInLine}
- renderDuplicationPopup={noop}
- snippet={sourceLines}
- />
- </SourceViewerContext.Provider>
+ <SnippetViewer
+ branchLike={undefined}
+ component={sourceViewerFile}
+ displayLineNumberOptions={false}
+ displaySCM={false}
+ expandBlock={(_i, direction) => props.onExpandBlock(direction)}
+ handleCloseIssues={noop}
+ handleOpenIssues={noop}
+ handleSymbolClick={props.onSymbolClick}
+ highlightedLocationMessage={undefined}
+ highlightedSymbols={highlightedSymbols}
+ index={0}
+ issue={hotspot}
+ issuesByLine={{}}
+ lastSnippetOfLastGroup={false}
+ locations={secondaryLocations}
+ locationsByLine={primaryLocations}
+ onIssueChange={noop}
+ onIssuePopupToggle={noop}
+ onLocationSelect={noop}
+ openIssuesByLine={{}}
+ renderAdditionalChildInLine={renderHotspotBoxInLine}
+ renderDuplicationPopup={noop}
+ snippet={sourceLines}
+ />
)}
</DeferredSpinner>
</div>
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 acc09b37d7c..98edc5eddf2 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
@@ -24,7 +24,7 @@ import { getSources } from '../../../../api/components';
import { mockBranch } from '../../../../helpers/mocks/branch-like';
import { mockComponent } from '../../../../helpers/mocks/component';
import { mockHotspot, mockHotspotComponent } from '../../../../helpers/mocks/security-hotspots';
-import { mockSourceLine } from '../../../../helpers/testMocks';
+import { mockFlowLocation, mockSourceLine } from '../../../../helpers/testMocks';
import { waitAndUpdate } from '../../../../helpers/testUtils';
import { ComponentQualifier } from '../../../../types/component';
import HotspotSnippetContainer from '../HotspotSnippetContainer';
@@ -44,12 +44,24 @@ it('should render correctly', () => {
it('should load sources on mount', async () => {
(getSources as jest.Mock).mockResolvedValueOnce(
- range(5, 18).map(line => mockSourceLine({ line }))
+ range(1, 20).map(line => mockSourceLine({ line }))
);
const hotspot = mockHotspot({
project: mockHotspotComponent({ branch: branch.name, qualifier: ComponentQualifier.Project }),
- textRange: { startLine: 10, endLine: 11, startOffset: 0, endOffset: 12 }
+ textRange: { startLine: 10, endLine: 11, startOffset: 0, endOffset: 12 },
+ flows: [
+ {
+ locations: [
+ mockFlowLocation({
+ textRange: { startLine: 8, endLine: 8, startOffset: 0, endOffset: 1 }
+ }),
+ mockFlowLocation({
+ textRange: { startLine: 13, endLine: 13, startOffset: 0, endOffset: 1 }
+ })
+ ]
+ }
+ ]
});
const wrapper = shallowRender({ hotspot });
@@ -58,11 +70,14 @@ it('should load sources on mount', async () => {
expect(getSources).toBeCalledWith(
expect.objectContaining({
- branch: branch.name
+ key: hotspot.component.key,
+ branch: branch.name,
+ from: 3,
+ to: 19
})
);
expect(wrapper.state().lastLine).toBeUndefined();
- expect(wrapper.state().sourceLines).toHaveLength(12);
+ expect(wrapper.state().sourceLines).toHaveLength(18);
});
it('should handle load sources failure', async () => {
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 07407eff2d0..7f1151397b0 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
@@ -56,6 +56,7 @@ function shallowRender(props?: Partial<HotspotSnippetContainerRendererProps>) {
onCommentButtonClick={jest.fn()}
onExpandBlock={jest.fn()}
onSymbolClick={jest.fn()}
+ secondaryLocations={[]}
sourceLines={[]}
sourceViewerFile={mockSourceViewerFile()}
{...props}
diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistoryAndComments-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistoryAndComments-test.tsx.snap
index ee97c8ccccc..89e707613bc 100644
--- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistoryAndComments-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotReviewHistoryAndComments-test.tsx.snap
@@ -68,6 +68,21 @@ exports[`should render correctly 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -157,6 +172,21 @@ exports[`should render correctly without user 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
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 622c59b6b2d..b2254d3d659 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
@@ -39,6 +39,21 @@ exports[`should render correctly 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -98,6 +113,21 @@ exports[`should render correctly 1`] = `
onCommentButtonClick={[MockFunction]}
onExpandBlock={[Function]}
onSymbolClick={[Function]}
+ secondaryLocations={
+ Array [
+ Object {
+ "component": "main.js",
+ "index": 0,
+ "text": undefined,
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ]
+ }
sourceLines={Array []}
sourceViewerFile={
Object {
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 beda736b7f9..615601d690d 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
@@ -89,155 +89,143 @@ exports[`should render correctly: with sourcelines 1`] = `
className="big-spacer"
loading={false}
>
- <ContextProvider
- value={
+ <SnippetViewer
+ component={
Object {
- "branchLike": Object {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": true,
- "name": "master",
- },
- "file": Object {
- "key": "foo",
- "measures": Object {
- "coverage": "85.2",
- "duplicationDensity": "1.0",
- "issues": "12",
- "lines": "56",
- },
- "path": "foo/bar.ts",
- "project": "my-project",
- "projectName": "MyProject",
- "q": "FIL",
- "uuid": "foo-bar",
+ "key": "foo",
+ "measures": Object {
+ "coverage": "85.2",
+ "duplicationDensity": "1.0",
+ "issues": "12",
+ "lines": "56",
},
+ "path": "foo/bar.ts",
+ "project": "my-project",
+ "projectName": "MyProject",
+ "q": "FIL",
+ "uuid": "foo-bar",
}
}
- >
- <SnippetViewer
- component={
- Object {
- "key": "foo",
- "measures": Object {
- "coverage": "85.2",
- "duplicationDensity": "1.0",
- "issues": "12",
- "lines": "56",
+ displayLineNumberOptions={false}
+ displaySCM={false}
+ expandBlock={[Function]}
+ handleCloseIssues={[Function]}
+ handleOpenIssues={[Function]}
+ handleSymbolClick={[MockFunction]}
+ highlightedSymbols={Array []}
+ index={0}
+ issue={
+ Object {
+ "assignee": "assignee",
+ "assigneeUser": Object {
+ "active": true,
+ "local": true,
+ "login": "assignee",
+ "name": "John Doe",
+ },
+ "author": "author",
+ "authorUser": Object {
+ "active": true,
+ "local": true,
+ "login": "author",
+ "name": "John Doe",
+ },
+ "canChangeStatus": true,
+ "changelog": Array [],
+ "comment": Array [],
+ "component": Object {
+ "key": "hotspot-component",
+ "longName": "Hotspot component long name",
+ "name": "Hotspot Component",
+ "path": "path/to/component",
+ "qualifier": "FIL",
+ },
+ "creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
},
- "path": "foo/bar.ts",
- "project": "my-project",
- "projectName": "MyProject",
- "q": "FIL",
- "uuid": "foo-bar",
- }
- }
- displayLineNumberOptions={false}
- displaySCM={false}
- expandBlock={[Function]}
- handleCloseIssues={[Function]}
- handleOpenIssues={[Function]}
- handleSymbolClick={[MockFunction]}
- highlightedSymbols={Array []}
- index={0}
- issue={
- Object {
- "assignee": "assignee",
- "assigneeUser": Object {
+ ],
+ "key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
+ "line": 142,
+ "message": "'3' is a magic number.",
+ "project": Object {
+ "key": "hotspot-component",
+ "longName": "Hotspot component long name",
+ "name": "Hotspot Component",
+ "path": "path/to/component",
+ "qualifier": "TRK",
+ },
+ "resolution": "FIXED",
+ "rule": Object {
+ "fixRecommendations": "<p>This a <strong>strong</strong> message about fixing !</p>",
+ "key": "squid:S2077",
+ "name": "That rule",
+ "riskDescription": "<p>This a <strong>strong</strong> message about risk !</p>",
+ "securityCategory": "sql-injection",
+ "vulnerabilityDescription": "<p>This a <strong>strong</strong> message about vulnerability !</p>",
+ "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",
},
- "author": "author",
- "authorUser": Object {
+ Object {
"active": true,
"local": true,
"login": "author",
"name": "John Doe",
},
- "canChangeStatus": true,
- "changelog": Array [],
- "comment": Array [],
- "component": Object {
- "key": "hotspot-component",
- "longName": "Hotspot component long name",
- "name": "Hotspot Component",
- "path": "path/to/component",
- "qualifier": "FIL",
- },
- "creationDate": "2013-05-13T17:55:41+0200",
- "key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
- "line": 142,
- "message": "'3' is a magic number.",
- "project": Object {
- "key": "hotspot-component",
- "longName": "Hotspot component long name",
- "name": "Hotspot Component",
- "path": "path/to/component",
- "qualifier": "TRK",
- },
- "resolution": "FIXED",
- "rule": Object {
- "fixRecommendations": "<p>This a <strong>strong</strong> message about fixing !</p>",
- "key": "squid:S2077",
- "name": "That rule",
- "riskDescription": "<p>This a <strong>strong</strong> message about risk !</p>",
- "securityCategory": "sql-injection",
- "vulnerabilityDescription": "<p>This a <strong>strong</strong> message about vulnerability !</p>",
- "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",
- },
- ],
- }
+ ],
}
- issuesByLine={Object {}}
- lastSnippetOfLastGroup={false}
- locations={Array []}
- locationsByLine={Object {}}
- onIssueChange={[Function]}
- onIssuePopupToggle={[Function]}
- onLocationSelect={[Function]}
- openIssuesByLine={Object {}}
- renderAdditionalChildInLine={[Function]}
- renderDuplicationPopup={[Function]}
- snippet={
- Array [
- Object {
- "code": "<span class=\\"k\\">import</span> java.util.<span class=\\"sym-9 sym\\">ArrayList</span>;",
- "coverageStatus": "covered",
- "coveredConditions": 2,
- "duplicated": false,
- "isNew": true,
- "line": 16,
- "scmAuthor": "simon.brandhof@sonarsource.com",
- "scmDate": "2018-12-11T10:48:39+0100",
- "scmRevision": "80f564becc0c0a1c9abaa006eca83a4fd278c3f0",
- },
- ]
- }
- />
- </ContextProvider>
+ }
+ issuesByLine={Object {}}
+ lastSnippetOfLastGroup={false}
+ locations={Array []}
+ locationsByLine={Object {}}
+ onIssueChange={[Function]}
+ onIssuePopupToggle={[Function]}
+ onLocationSelect={[Function]}
+ openIssuesByLine={Object {}}
+ renderAdditionalChildInLine={[Function]}
+ renderDuplicationPopup={[Function]}
+ snippet={
+ Array [
+ Object {
+ "code": "<span class=\\"k\\">import</span> java.util.<span class=\\"sym-9 sym\\">ArrayList</span>;",
+ "coverageStatus": "covered",
+ "coveredConditions": 2,
+ "duplicated": false,
+ "isNew": true,
+ "line": 16,
+ "scmAuthor": "simon.brandhof@sonarsource.com",
+ "scmDate": "2018-12-11T10:48:39+0100",
+ "scmRevision": "80f564becc0c0a1c9abaa006eca83a4fd278c3f0",
+ },
+ ]
+ }
+ />
</DeferredSpinner>
</div>
</Fragment>
diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotViewerRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotViewerRenderer-test.tsx.snap
index f24651eb644..780f88703c2 100644
--- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotViewerRenderer-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotViewerRenderer-test.tsx.snap
@@ -149,6 +149,21 @@ exports[`should render correctly: anonymous user 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -229,6 +244,21 @@ exports[`should render correctly: anonymous user 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -327,6 +357,21 @@ exports[`should render correctly: anonymous user 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -401,6 +446,21 @@ exports[`should render correctly: anonymous user 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -484,6 +544,21 @@ exports[`should render correctly: anonymous user 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -683,6 +758,21 @@ exports[`should render correctly: assignee without name 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -763,6 +853,21 @@ exports[`should render correctly: assignee without name 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -861,6 +966,21 @@ exports[`should render correctly: assignee without name 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -935,6 +1055,21 @@ exports[`should render correctly: assignee without name 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -1018,6 +1153,21 @@ exports[`should render correctly: assignee without name 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -1217,6 +1367,21 @@ exports[`should render correctly: default 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -1297,6 +1462,21 @@ exports[`should render correctly: default 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -1395,6 +1575,21 @@ exports[`should render correctly: default 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -1469,6 +1664,21 @@ exports[`should render correctly: default 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -1552,6 +1762,21 @@ exports[`should render correctly: default 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -1751,6 +1976,21 @@ exports[`should render correctly: deleted assignee 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -1831,6 +2071,21 @@ exports[`should render correctly: deleted assignee 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -1929,6 +2184,21 @@ exports[`should render correctly: deleted assignee 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -2003,6 +2273,21 @@ exports[`should render correctly: deleted assignee 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -2086,6 +2371,21 @@ exports[`should render correctly: deleted assignee 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -2298,6 +2598,21 @@ exports[`should render correctly: show success modal 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -2378,6 +2693,21 @@ exports[`should render correctly: show success modal 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -2476,6 +2806,21 @@ exports[`should render correctly: show success modal 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -2550,6 +2895,21 @@ exports[`should render correctly: show success modal 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -2633,6 +2993,21 @@ exports[`should render correctly: show success modal 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -2832,6 +3207,21 @@ exports[`should render correctly: unassigned 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -2912,6 +3302,21 @@ exports[`should render correctly: unassigned 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -3010,6 +3415,21 @@ exports[`should render correctly: unassigned 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -3084,6 +3504,21 @@ exports[`should render correctly: unassigned 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -3167,6 +3602,21 @@ exports[`should render correctly: unassigned 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/status/__tests__/__snapshots__/Status-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/status/__tests__/__snapshots__/Status-test.tsx.snap
index 610a011c7cd..3dc6f22b2ce 100644
--- a/server/sonar-web/src/main/js/apps/security-hotspots/components/status/__tests__/__snapshots__/Status-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/status/__tests__/__snapshots__/Status-test.tsx.snap
@@ -54,6 +54,21 @@ exports[`should render correctly: closed 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -177,6 +192,21 @@ exports[`should render correctly: open 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
@@ -300,6 +330,21 @@ exports[`should render correctly: readonly 1`] = `
"qualifier": "FIL",
},
"creationDate": "2013-05-13T17:55:41+0200",
+ "flows": Array [
+ Object {
+ "locations": Array [
+ Object {
+ "component": "main.js",
+ "textRange": Object {
+ "endLine": 2,
+ "endOffset": 2,
+ "startLine": 1,
+ "startOffset": 1,
+ },
+ },
+ ],
+ },
+ ],
"key": "01fc972e-2a3c-433e-bcae-0bd7f88f5123",
"line": 142,
"message": "'3' is a magic number.",
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/LineCode.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/LineCode.tsx
index 8904fc41747..8a65af22ba2 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/LineCode.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/LineCode.tsx
@@ -22,7 +22,7 @@ import * as React from 'react';
import { BranchLike } from '../../../types/branch-like';
import { Issue, LinearIssueLocation, SourceLine } from '../../../types/types';
import LocationIndex from '../../common/LocationIndex';
-import LocationMessage from '../../common/LocationMessage';
+import Tooltip from '../../controls/Tooltip';
import {
highlightIssueLocations,
highlightSymbol,
@@ -141,15 +141,18 @@ export default class LineCode extends React.PureComponent<Props, State> {
const { onLocationSelect } = this.props;
const onClick = onLocationSelect ? () => onLocationSelect(index) : undefined;
const ref = selected ? (node: HTMLElement | null) => (this.activeMarkerNode = node) : undefined;
+
return (
- <LocationIndex
- key={`marker-${index}`}
- leading={leading}
- onClick={onClick}
- selected={selected}>
- <span ref={ref}>{index + 1}</span>
- {message && <LocationMessage selected={true}>{message}</LocationMessage>}
- </LocationIndex>
+ <Tooltip overlay={message} placement="top">
+ <LocationIndex
+ key={`marker-${index}`}
+ leading={leading}
+ onClick={onClick}
+ selected={selected}
+ aria-label={message ? `${index + 1}-${message}` : index + 1}>
+ <span ref={ref}>{index + 1}</span>
+ </LocationIndex>
+ </Tooltip>
);
}
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineCode-test.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineCode-test.tsx
index ebe3cc56430..92ee72265e2 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineCode-test.tsx
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineCode-test.tsx
@@ -28,6 +28,13 @@ it('render code', () => {
expect(shallowRender({ additionalChild: <div>additional child</div> })).toMatchSnapshot(
'with additional child'
);
+ expect(
+ shallowRender({
+ secondaryIssueLocations: [
+ { index: 1, from: 5, to: 6, line: 16, startLine: 16, text: 'secondary-location-msg' }
+ ]
+ })
+ ).toMatchSnapshot('with secondary location');
});
function shallowRender(props: Partial<LineCode['props']> = {}) {
diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineCode-test.tsx.snap b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineCode-test.tsx.snap
index 77bebd4c983..37718ed25a8 100644
--- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineCode-test.tsx.snap
+++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineCode-test.tsx.snap
@@ -242,3 +242,139 @@ exports[`render code: with additional child 1`] = `
</div>
</td>
`;
+
+exports[`render code: with secondary location 1`] = `
+<td
+ className="source-line-code code has-issues"
+ data-line-number={16}
+>
+ <div
+ className="source-line-code-inner"
+ >
+ <pre>
+ <span
+ className="k source-line-code-issue"
+ key="0"
+ >
+ impor
+ </span>
+ <Tooltip
+ overlay="secondary-location-msg"
+ placement="top"
+ >
+ <LocationIndex
+ aria-label="2-secondary-location-msg"
+ key="marker-1"
+ leading={false}
+ onClick={[Function]}
+ selected={false}
+ >
+ <span>
+ 2
+ </span>
+ </LocationIndex>
+ </Tooltip>
+ <span
+ className="k issue-location"
+ key="1"
+ >
+ t
+ </span>
+ <span
+ className=""
+ key="2"
+ >
+ java.util.
+ </span>
+ <span
+ className="sym-9 sym highlighted"
+ key="3"
+ >
+ ArrayList
+ </span>
+ <span
+ className=""
+ key="4"
+ >
+ ;
+ </span>
+ </pre>
+ </div>
+ <LineIssuesList
+ branchLike={
+ Object {
+ "analysisDate": "2018-01-01",
+ "excludedFromPurge": true,
+ "isMain": false,
+ "name": "branch-6.7",
+ }
+ }
+ issues={
+ Array [
+ Object {
+ "actions": Array [],
+ "component": "main.js",
+ "componentLongName": "main.js",
+ "componentQualifier": "FIL",
+ "componentUuid": "foo1234",
+ "creationDate": "2017-03-01T09:36:01+0100",
+ "flows": Array [],
+ "fromHotspot": false,
+ "key": "issue-1",
+ "line": 25,
+ "message": "Reduce the number of conditional operators (4) used in the expression",
+ "project": "myproject",
+ "projectKey": "foo",
+ "projectName": "Foo",
+ "rule": "javascript:S1067",
+ "ruleName": "foo",
+ "secondaryLocations": Array [],
+ "severity": "MAJOR",
+ "status": "OPEN",
+ "textRange": Object {
+ "endLine": 26,
+ "endOffset": 15,
+ "startLine": 25,
+ "startOffset": 0,
+ },
+ "transitions": Array [],
+ "type": "BUG",
+ },
+ Object {
+ "actions": Array [],
+ "component": "main.js",
+ "componentLongName": "main.js",
+ "componentQualifier": "FIL",
+ "componentUuid": "foo1234",
+ "creationDate": "2017-03-01T09:36:01+0100",
+ "flows": Array [],
+ "fromHotspot": false,
+ "key": "issue-2",
+ "line": 25,
+ "message": "Reduce the number of conditional operators (4) used in the expression",
+ "project": "myproject",
+ "projectKey": "foo",
+ "projectName": "Foo",
+ "rule": "javascript:S1067",
+ "ruleName": "foo",
+ "secondaryLocations": Array [],
+ "severity": "MAJOR",
+ "status": "OPEN",
+ "textRange": Object {
+ "endLine": 26,
+ "endOffset": 15,
+ "startLine": 25,
+ "startOffset": 0,
+ },
+ "transitions": Array [],
+ "type": "BUG",
+ },
+ ]
+ }
+ onIssueChange={[MockFunction]}
+ onIssueClick={[MockFunction]}
+ onIssuePopupToggle={[MockFunction]}
+ selectedIssue="issue-1"
+ />
+</td>
+`;
diff --git a/server/sonar-web/src/main/js/helpers/mocks/security-hotspots.ts b/server/sonar-web/src/main/js/helpers/mocks/security-hotspots.ts
index 391dd38edec..d07d65d739b 100644
--- a/server/sonar-web/src/main/js/helpers/mocks/security-hotspots.ts
+++ b/server/sonar-web/src/main/js/helpers/mocks/security-hotspots.ts
@@ -31,7 +31,7 @@ import {
ReviewHistoryType,
RiskExposure
} from '../../types/security-hotspots';
-import { mockUser } from '../testMocks';
+import { mockFlowLocation, mockUser } from '../testMocks';
export function mockRawHotspot(overrides: Partial<RawHotspot> = {}): RawHotspot {
return {
@@ -65,6 +65,7 @@ export function mockHotspot(overrides?: Partial<Hotspot>): Hotspot {
comment: [],
component: mockHotspotComponent({ qualifier: ComponentQualifier.File }),
creationDate: '2013-05-13T17:55:41+0200',
+ flows: [{ locations: [mockFlowLocation()] }],
key: '01fc972e-2a3c-433e-bcae-0bd7f88f5123',
line: 142,
message: "'3' is a magic number.",
diff --git a/server/sonar-web/src/main/js/types/security-hotspots.ts b/server/sonar-web/src/main/js/types/security-hotspots.ts
index ecd85abcda1..8d0a15fcde0 100644
--- a/server/sonar-web/src/main/js/types/security-hotspots.ts
+++ b/server/sonar-web/src/main/js/types/security-hotspots.ts
@@ -18,7 +18,14 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { ComponentQualifier } from './component';
-import { IssueChangelog, IssueChangelogDiff, Paging, TextRange, UserBase } from './types';
+import {
+ FlowLocation,
+ IssueChangelog,
+ IssueChangelogDiff,
+ Paging,
+ TextRange,
+ UserBase
+} from './types';
export enum RiskExposure {
LOW = 'LOW',
@@ -85,6 +92,7 @@ export interface Hotspot {
comment: HotspotComment[];
component: HotspotComponent;
creationDate: string;
+ flows: { locations: FlowLocation[] }[];
key: string;
line?: number;
message: string;