diff options
20 files changed, 294 insertions, 651 deletions
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetGroupViewer.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetGroupViewer.tsx index 37afefb3222..aad18317be3 100644 --- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetGroupViewer.tsx +++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetGroupViewer.tsx @@ -21,6 +21,7 @@ import { noop } from 'lodash'; import * as React from 'react'; import { getSources } from '../../../api/components'; import Issue from '../../../components/issue/Issue'; +import LineIssuesList from '../../../components/SourceViewer/components/LineIssuesList'; import getCoverageStatus from '../../../components/SourceViewer/helpers/getCoverageStatus'; import { locationsByLine } from '../../../components/SourceViewer/helpers/indexing'; import SourceViewerHeaderSlim from '../../../components/SourceViewer/SourceViewerHeaderSlim'; @@ -315,6 +316,39 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone return this.props.renderDuplicationPopup(this.props.snippetGroup.component, index, line); }; + renderIssuesList = (line: SourceLine) => { + const { openIssuesByLine } = this.state; + + const { isLastOccurenceOfPrimaryComponent, issue, issuesByLine, snippetGroup } = this.props; + const locations = + issue.component === snippetGroup.component.key && issue.textRange !== undefined + ? locationsByLine([issue]) + : {}; + + const isFlow = issue.secondaryLocations.length === 0; + const includeIssueLocation = isFlow ? isLastOccurenceOfPrimaryComponent : true; + + const issuesForLine = issuesByLine[line.line] || []; + + const selectedIssue = issuesForLine.find(i => i.key === issue.key)?.key; + + const issueLocationsByLine = includeIssueLocation ? locations : {}; + return ( + <LineIssuesList + issueLocationsByLine={issueLocationsByLine} + issuesForLine={issuesForLine} + line={line} + openIssuesByLine={openIssuesByLine} + branchLike={this.props.branchLike} + issuePopup={this.props.issuePopup} + onIssueChange={this.props.onIssueChange} + onIssueClick={noop} + onIssuePopupToggle={this.props.onIssuePopupToggle} + selectedIssue={selectedIssue} + /> + ); + }; + renderSnippet({ index, issuesByLine, @@ -330,7 +364,7 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone }) { return ( <SnippetViewer - branchLike={this.props.branchLike} + renderAdditionalChildInLine={this.renderIssuesList} component={this.props.snippetGroup.component} duplications={this.props.duplications} duplicationsByLine={this.props.duplicationsByLine} @@ -342,14 +376,11 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone highlightedSymbols={this.state.highlightedSymbols} index={index} issue={this.props.issue} - issuePopup={this.props.issuePopup} issuesByLine={issuesByLine} lastSnippetOfLastGroup={lastSnippetOfLastGroup} loadDuplications={this.loadDuplications} locations={this.props.locations} locationsByLine={locationsByLine} - onIssueChange={this.props.onIssueChange} - onIssuePopupToggle={this.props.onIssuePopupToggle} onLocationSelect={this.props.onLocationSelect} openIssuesByLine={this.state.openIssuesByLine} renderDuplicationPopup={this.renderDuplicationPopup} diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.tsx index afee226ce59..07a0685f534 100644 --- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.tsx +++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.tsx @@ -25,12 +25,10 @@ import { symbolsByLine } from '../../../components/SourceViewer/helpers/indexing import { getSecondaryIssueLocationsForLine } from '../../../components/SourceViewer/helpers/issueLocations'; import { optimizeHighlightedSymbols, - optimizeLocationMessage, - optimizeSelectedIssue + optimizeLocationMessage } from '../../../components/SourceViewer/helpers/lines'; import { translate } from '../../../helpers/l10n'; import { scrollHorizontally } from '../../../helpers/scrolling'; -import { BranchLike } from '../../../types/branch-like'; import { Dict, Duplication, @@ -46,7 +44,6 @@ import './SnippetViewer.css'; import { inSnippet, LINES_BELOW_ISSUE } from './utils'; interface Props { - branchLike: BranchLike | undefined; component: SourceViewerFile; displayLineNumberOptions?: boolean; displaySCM?: boolean; @@ -60,17 +57,14 @@ interface Props { highlightedSymbols: string[]; index: number; issue: Pick<Issue, 'key' | 'textRange' | 'line'>; - issuePopup?: { issue: string; name: string }; issuesByLine: IssuesByLine; lastSnippetOfLastGroup: boolean; loadDuplications?: (line: SourceLine) => void; locations: FlowLocation[]; locationsByLine: { [line: number]: LinearIssueLocation[] }; - onIssueChange: (issue: Issue) => void; - onIssuePopupToggle: (issue: string, popupName: string, open?: boolean) => void; onLocationSelect: (index: number) => void; openIssuesByLine: Dict<boolean>; - renderAdditionalChildInLine?: (lineNumber: number) => React.ReactNode | undefined; + renderAdditionalChildInLine?: (line: SourceLine) => React.ReactNode | undefined; renderDuplicationPopup: (index: number, line: number) => React.ReactNode; scroll?: (element: HTMLElement, offset?: number) => void; snippet: SourceLine[]; @@ -154,11 +148,6 @@ export default class SnippetViewer extends React.PureComponent<Props> { return ( <Line - additionalChild={ - this.props.renderAdditionalChildInLine && - this.props.renderAdditionalChildInLine(line.line) - } - branchLike={this.props.branchLike} displayAllIssues={false} displayCoverage={true} displayDuplications={displayDuplications} @@ -176,14 +165,11 @@ export default class SnippetViewer extends React.PureComponent<Props> { )} highlightedSymbols={optimizeHighlightedSymbols(symbols, this.props.highlightedSymbols)} issueLocations={issueLocations} - issuePopup={this.props.issuePopup} issues={issuesForLine} key={line.line} last={false} line={line} loadDuplications={this.props.loadDuplications || noop} - onIssueChange={this.props.onIssueChange} - onIssuePopupToggle={this.props.onIssuePopupToggle} onIssueSelect={noop} onIssueUnselect={noop} onIssuesClose={this.props.handleCloseIssues} @@ -195,9 +181,9 @@ export default class SnippetViewer extends React.PureComponent<Props> { renderDuplicationPopup={this.props.renderDuplicationPopup} scroll={this.doScroll} secondaryIssueLocations={secondaryIssueLocations} - selectedIssue={optimizeSelectedIssue(this.props.issue.key, issuesForLine)} - verticalBuffer={verticalBuffer} - /> + verticalBuffer={verticalBuffer}> + {this.props.renderAdditionalChildInLine && this.props.renderAdditionalChildInLine(line)} + </Line> ); } diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/ComponentSourceSnippetGroupViewer-test.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/ComponentSourceSnippetGroupViewer-test.tsx index cc815732c63..5db19b60cc0 100644 --- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/ComponentSourceSnippetGroupViewer-test.tsx +++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/ComponentSourceSnippetGroupViewer-test.tsx @@ -291,6 +291,19 @@ it('should correctly handle lines actions', () => { ); }); +it('should render correctly line with issue', () => { + const issue = mockIssue(false, { + textRange: { endLine: 1, startLine: 1, endOffset: 1, startOffset: 0 } + }); + const wrapper = shallowRender({ + issue, + issuesByLine: { '1': [issue] } + }); + wrapper.instance().setState({ openIssuesByLine: { '1': true } }); + const wrapperLine = shallow(wrapper.instance().renderIssuesList(mockSourceLine({ line: 1 }))); + expect(wrapperLine).toMatchSnapshot(); +}); + describe('getNodes', () => { const snippetGroup: SnippetGroup = { component: mockSourceViewerFile(), diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/SnippetViewer-test.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/SnippetViewer-test.tsx index 10600b2a642..76fb05d7c18 100644 --- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/SnippetViewer-test.tsx +++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/SnippetViewer-test.tsx @@ -20,7 +20,6 @@ import { mount, shallow } from 'enzyme'; import { range } from 'lodash'; import * as React from 'react'; -import { mockMainBranch } from '../../../../helpers/mocks/branch-like'; import { scrollHorizontally } from '../../../../helpers/scrolling'; import { mockIssue, mockSourceLine, mockSourceViewerFile } from '../../../../helpers/testMocks'; import SnippetViewer from '../SnippetViewer'; @@ -59,7 +58,7 @@ it('should render additional child in line', () => { const renderAdditionalChildInLine = jest.fn().mockReturnValue(child); const wrapper = shallowRender({ renderAdditionalChildInLine, snippet: [sourceline] }); - const renderedLine = wrapper.instance().renderLine({ + wrapper.instance().renderLine({ displayDuplications: false, index: 1, issuesForLine: [], @@ -70,8 +69,7 @@ it('should render additional child in line', () => { verticalBuffer: 5 }); - expect(renderAdditionalChildInLine).toBeCalledWith(42); - expect(renderedLine.props.additionalChild).toBe(child); + expect(renderAdditionalChildInLine).toBeCalledWith(sourceline); }); it('should render correctly when at the top of the file', () => { @@ -143,7 +141,6 @@ it('should handle scrolling to expanded row', () => { function shallowRender(props: Partial<SnippetViewer['props']> = {}) { return shallow<SnippetViewer>( <SnippetViewer - branchLike={mockMainBranch()} component={mockSourceViewerFile()} duplications={undefined} duplicationsByLine={undefined} @@ -160,8 +157,6 @@ function shallowRender(props: Partial<SnippetViewer['props']> = {}) { loadDuplications={jest.fn()} locations={[]} locationsByLine={{}} - onIssueChange={jest.fn()} - onIssuePopupToggle={jest.fn()} onLocationSelect={jest.fn()} openIssuesByLine={{}} renderDuplicationPopup={jest.fn()} @@ -175,7 +170,6 @@ function shallowRender(props: Partial<SnippetViewer['props']> = {}) { function mountRender(props: Partial<SnippetViewer['props']> = {}) { return mount<SnippetViewer>( <SnippetViewer - branchLike={mockMainBranch()} component={mockSourceViewerFile()} duplications={undefined} duplicationsByLine={undefined} @@ -192,8 +186,6 @@ function mountRender(props: Partial<SnippetViewer['props']> = {}) { loadDuplications={jest.fn()} locations={[]} locationsByLine={{}} - onIssueChange={jest.fn()} - onIssuePopupToggle={jest.fn()} onLocationSelect={jest.fn()} openIssuesByLine={{}} renderDuplicationPopup={jest.fn()} diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/ComponentSourceSnippetGroupViewer-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/ComponentSourceSnippetGroupViewer-test.tsx.snap index 49f004c7337..4d100ae4cf3 100644 --- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/ComponentSourceSnippetGroupViewer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/ComponentSourceSnippetGroupViewer-test.tsx.snap @@ -35,3 +35,56 @@ exports[`should render correctly 1`] = ` /> </div> `; + +exports[`should render correctly line with issue 1`] = ` +<div + className="issue-list" +> + <Issue + branchLike={ + Object { + "analysisDate": "2018-01-01", + "excludedFromPurge": true, + "isMain": true, + "name": "master", + } + } + issue={ + 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": "AVsae-CQS-9G3txfbFN2", + "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": 1, + "endOffset": 1, + "startLine": 1, + "startOffset": 0, + }, + "transitions": Array [], + "type": "BUG", + } + } + key="AVsae-CQS-9G3txfbFN2" + onChange={[MockFunction]} + onClick={[Function]} + onPopupToggle={[MockFunction]} + selected={true} + /> +</div> +`; diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/SnippetViewer-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/SnippetViewer-test.tsx.snap index 54632e972dc..804854852ec 100644 --- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/SnippetViewer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/SnippetViewer-test.tsx.snap @@ -21,14 +21,6 @@ exports[`should render correctly 1`] = ` > <tbody> <Line - branchLike={ - Object { - "analysisDate": "2018-01-01", - "excludedFromPurge": true, - "isMain": true, - "name": "master", - } - } displayAllIssues={false} displayCoverage={true} displayDuplications={false} @@ -56,8 +48,6 @@ exports[`should render correctly 1`] = ` } } loadDuplications={[MockFunction]} - onIssueChange={[MockFunction]} - onIssuePopupToggle={[MockFunction]} onIssueSelect={[Function]} onIssueUnselect={[Function]} onIssuesClose={[MockFunction]} @@ -70,14 +60,6 @@ exports[`should render correctly 1`] = ` verticalBuffer={0} /> <Line - branchLike={ - Object { - "analysisDate": "2018-01-01", - "excludedFromPurge": true, - "isMain": true, - "name": "master", - } - } displayAllIssues={false} displayCoverage={true} displayDuplications={false} @@ -105,8 +87,6 @@ exports[`should render correctly 1`] = ` } } loadDuplications={[MockFunction]} - onIssueChange={[MockFunction]} - onIssuePopupToggle={[MockFunction]} onIssueSelect={[Function]} onIssueUnselect={[Function]} onIssuesClose={[MockFunction]} @@ -132,14 +112,6 @@ exports[`should render correctly 1`] = ` verticalBuffer={0} /> <Line - branchLike={ - Object { - "analysisDate": "2018-01-01", - "excludedFromPurge": true, - "isMain": true, - "name": "master", - } - } displayAllIssues={false} displayCoverage={true} displayDuplications={false} @@ -167,8 +139,6 @@ exports[`should render correctly 1`] = ` } } loadDuplications={[MockFunction]} - onIssueChange={[MockFunction]} - onIssuePopupToggle={[MockFunction]} onIssueSelect={[Function]} onIssueUnselect={[Function]} onIssuesClose={[MockFunction]} @@ -231,14 +201,6 @@ exports[`should render correctly when at the bottom of the file 1`] = ` > <tbody> <Line - branchLike={ - Object { - "analysisDate": "2018-01-01", - "excludedFromPurge": true, - "isMain": true, - "name": "master", - } - } displayAllIssues={false} displayCoverage={true} displayDuplications={false} @@ -266,8 +228,6 @@ exports[`should render correctly when at the bottom of the file 1`] = ` } } loadDuplications={[MockFunction]} - onIssueChange={[MockFunction]} - onIssuePopupToggle={[MockFunction]} onIssueSelect={[Function]} onIssueUnselect={[Function]} onIssuesClose={[MockFunction]} @@ -280,14 +240,6 @@ exports[`should render correctly when at the bottom of the file 1`] = ` verticalBuffer={0} /> <Line - branchLike={ - Object { - "analysisDate": "2018-01-01", - "excludedFromPurge": true, - "isMain": true, - "name": "master", - } - } displayAllIssues={false} displayCoverage={true} displayDuplications={false} @@ -315,8 +267,6 @@ exports[`should render correctly when at the bottom of the file 1`] = ` } } loadDuplications={[MockFunction]} - onIssueChange={[MockFunction]} - onIssuePopupToggle={[MockFunction]} onIssueSelect={[Function]} onIssueUnselect={[Function]} onIssuesClose={[MockFunction]} @@ -342,14 +292,6 @@ exports[`should render correctly when at the bottom of the file 1`] = ` verticalBuffer={0} /> <Line - branchLike={ - Object { - "analysisDate": "2018-01-01", - "excludedFromPurge": true, - "isMain": true, - "name": "master", - } - } displayAllIssues={false} displayCoverage={true} displayDuplications={false} @@ -377,8 +319,6 @@ exports[`should render correctly when at the bottom of the file 1`] = ` } } loadDuplications={[MockFunction]} - onIssueChange={[MockFunction]} - onIssuePopupToggle={[MockFunction]} onIssueSelect={[Function]} onIssueUnselect={[Function]} onIssuesClose={[MockFunction]} @@ -404,14 +344,6 @@ exports[`should render correctly when at the bottom of the file 1`] = ` verticalBuffer={0} /> <Line - branchLike={ - Object { - "analysisDate": "2018-01-01", - "excludedFromPurge": true, - "isMain": true, - "name": "master", - } - } displayAllIssues={false} displayCoverage={true} displayDuplications={false} @@ -439,8 +371,6 @@ exports[`should render correctly when at the bottom of the file 1`] = ` } } loadDuplications={[MockFunction]} - onIssueChange={[MockFunction]} - onIssuePopupToggle={[MockFunction]} onIssueSelect={[Function]} onIssueUnselect={[Function]} onIssuesClose={[MockFunction]} @@ -492,14 +422,6 @@ exports[`should render correctly when at the top of the file 1`] = ` > <tbody> <Line - branchLike={ - Object { - "analysisDate": "2018-01-01", - "excludedFromPurge": true, - "isMain": true, - "name": "master", - } - } displayAllIssues={false} displayCoverage={true} displayDuplications={false} @@ -527,8 +449,6 @@ exports[`should render correctly when at the top of the file 1`] = ` } } loadDuplications={[MockFunction]} - onIssueChange={[MockFunction]} - onIssuePopupToggle={[MockFunction]} onIssueSelect={[Function]} onIssueUnselect={[Function]} onIssuesClose={[MockFunction]} @@ -541,14 +461,6 @@ exports[`should render correctly when at the top of the file 1`] = ` verticalBuffer={0} /> <Line - branchLike={ - Object { - "analysisDate": "2018-01-01", - "excludedFromPurge": true, - "isMain": true, - "name": "master", - } - } displayAllIssues={false} displayCoverage={true} displayDuplications={false} @@ -576,8 +488,6 @@ exports[`should render correctly when at the top of the file 1`] = ` } } loadDuplications={[MockFunction]} - onIssueChange={[MockFunction]} - onIssuePopupToggle={[MockFunction]} onIssueSelect={[Function]} onIssueUnselect={[Function]} onIssuesClose={[MockFunction]} @@ -603,14 +513,6 @@ exports[`should render correctly when at the top of the file 1`] = ` verticalBuffer={0} /> <Line - branchLike={ - Object { - "analysisDate": "2018-01-01", - "excludedFromPurge": true, - "isMain": true, - "name": "master", - } - } displayAllIssues={false} displayCoverage={true} displayDuplications={false} @@ -638,8 +540,6 @@ exports[`should render correctly when at the top of the file 1`] = ` } } loadDuplications={[MockFunction]} - onIssueChange={[MockFunction]} - onIssuePopupToggle={[MockFunction]} onIssueSelect={[Function]} onIssueUnselect={[Function]} onIssuesClose={[MockFunction]} @@ -665,14 +565,6 @@ exports[`should render correctly when at the top of the file 1`] = ` verticalBuffer={0} /> <Line - branchLike={ - Object { - "analysisDate": "2018-01-01", - "excludedFromPurge": true, - "isMain": true, - "name": "master", - } - } displayAllIssues={false} displayCoverage={true} displayDuplications={false} @@ -700,8 +592,6 @@ exports[`should render correctly when at the top of the file 1`] = ` } } loadDuplications={[MockFunction]} - onIssueChange={[MockFunction]} - onIssuePopupToggle={[MockFunction]} onIssueSelect={[Function]} onIssueUnselect={[Function]} onIssuesClose={[MockFunction]} @@ -727,14 +617,6 @@ exports[`should render correctly when at the top of the file 1`] = ` verticalBuffer={0} /> <Line - branchLike={ - Object { - "analysisDate": "2018-01-01", - "excludedFromPurge": true, - "isMain": true, - "name": "master", - } - } displayAllIssues={false} displayCoverage={true} displayDuplications={false} @@ -762,8 +644,6 @@ exports[`should render correctly when at the top of the file 1`] = ` } } loadDuplications={[MockFunction]} - onIssueChange={[MockFunction]} - onIssuePopupToggle={[MockFunction]} onIssueSelect={[Function]} onIssueUnselect={[Function]} onIssuesClose={[MockFunction]} @@ -789,14 +669,6 @@ exports[`should render correctly when at the top of the file 1`] = ` verticalBuffer={0} /> <Line - branchLike={ - Object { - "analysisDate": "2018-01-01", - "excludedFromPurge": true, - "isMain": true, - "name": "master", - } - } displayAllIssues={false} displayCoverage={true} displayDuplications={false} @@ -824,8 +696,6 @@ exports[`should render correctly when at the top of the file 1`] = ` } } loadDuplications={[MockFunction]} - onIssueChange={[MockFunction]} - onIssuePopupToggle={[MockFunction]} onIssueSelect={[Function]} onIssueUnselect={[Function]} onIssuesClose={[MockFunction]} @@ -851,14 +721,6 @@ exports[`should render correctly when at the top of the file 1`] = ` verticalBuffer={0} /> <Line - branchLike={ - Object { - "analysisDate": "2018-01-01", - "excludedFromPurge": true, - "isMain": true, - "name": "master", - } - } displayAllIssues={false} displayCoverage={true} displayDuplications={false} @@ -886,8 +748,6 @@ exports[`should render correctly when at the top of the file 1`] = ` } } loadDuplications={[MockFunction]} - onIssueChange={[MockFunction]} - onIssuePopupToggle={[MockFunction]} onIssueSelect={[Function]} onIssueUnselect={[Function]} onIssuesClose={[MockFunction]} @@ -950,14 +810,6 @@ exports[`should render correctly with no SCM 1`] = ` > <tbody> <Line - branchLike={ - Object { - "analysisDate": "2018-01-01", - "excludedFromPurge": true, - "isMain": true, - "name": "master", - } - } displayAllIssues={false} displayCoverage={true} displayDuplications={false} @@ -986,8 +838,6 @@ exports[`should render correctly with no SCM 1`] = ` } } loadDuplications={[MockFunction]} - onIssueChange={[MockFunction]} - onIssuePopupToggle={[MockFunction]} onIssueSelect={[Function]} onIssueUnselect={[Function]} onIssuesClose={[MockFunction]} @@ -1000,14 +850,6 @@ exports[`should render correctly with no SCM 1`] = ` verticalBuffer={0} /> <Line - branchLike={ - Object { - "analysisDate": "2018-01-01", - "excludedFromPurge": true, - "isMain": true, - "name": "master", - } - } displayAllIssues={false} displayCoverage={true} displayDuplications={false} @@ -1036,8 +878,6 @@ exports[`should render correctly with no SCM 1`] = ` } } loadDuplications={[MockFunction]} - onIssueChange={[MockFunction]} - onIssuePopupToggle={[MockFunction]} onIssueSelect={[Function]} onIssueUnselect={[Function]} onIssuesClose={[MockFunction]} @@ -1063,14 +903,6 @@ exports[`should render correctly with no SCM 1`] = ` verticalBuffer={0} /> <Line - branchLike={ - Object { - "analysisDate": "2018-01-01", - "excludedFromPurge": true, - "isMain": true, - "name": "master", - } - } displayAllIssues={false} displayCoverage={true} displayDuplications={false} @@ -1099,8 +931,6 @@ exports[`should render correctly with no SCM 1`] = ` } } loadDuplications={[MockFunction]} - onIssueChange={[MockFunction]} - onIssuePopupToggle={[MockFunction]} onIssueSelect={[Function]} onIssueUnselect={[Function]} onIssuesClose={[MockFunction]} 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 49d6d804a73..7cc9c62cfe9 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 @@ -108,8 +108,8 @@ export default function HotspotSnippetContainerRenderer( [hotspot, secondaryLocationSelected, props.onCommentButtonClick] ); - const renderHotspotBoxInLine = (lineNumber: number) => - lineNumber === hotspot.line ? hotspotPrimaryLocationBox : undefined; + const renderHotspotBoxInLine = (line: SourceLine) => + line.line === hotspot.line ? hotspotPrimaryLocationBox : undefined; const highlightedLocation = selectedHotspotLocation !== undefined @@ -126,7 +126,6 @@ export default function HotspotSnippetContainerRenderer( <DeferredSpinner className="big-spacer" loading={loading}> {sourceLines.length > 0 && ( <SnippetViewer - branchLike={undefined} component={sourceViewerFile} displayLineNumberOptions={false} displaySCM={false} @@ -142,8 +141,6 @@ export default function HotspotSnippetContainerRenderer( lastSnippetOfLastGroup={false} locations={secondaryLocations} locationsByLine={primaryLocations} - onIssueChange={noop} - onIssuePopupToggle={noop} onLocationSelect={props.onLocationSelect} openIssuesByLine={{}} renderAdditionalChildInLine={renderHotspotBoxInLine} 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 69178af3cb2..ac4ad4baab8 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 @@ -51,8 +51,8 @@ it('should render a HotspotPrimaryLocationBox', () => { const { renderAdditionalChildInLine } = wrapper.find(SnippetViewer).props(); - expect(renderAdditionalChildInLine!(10)).toBeUndefined(); - expect(renderAdditionalChildInLine!(42)).not.toBeUndefined(); + expect(renderAdditionalChildInLine!(mockSourceLine({ line: 10 }))).toBeUndefined(); + expect(renderAdditionalChildInLine!(mockSourceLine({ line: 42 }))).not.toBeUndefined(); }); it('should render correctly when secondary location is selected', () => { 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 65a827e5192..be77b3e2008 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 @@ -514,8 +514,6 @@ exports[`should render correctly: with sourcelines 1`] = ` lastSnippetOfLastGroup={false} locations={Array []} locationsByLine={Object {}} - onIssueChange={[Function]} - onIssuePopupToggle={[Function]} onLocationSelect={[MockFunction]} openIssuesByLine={Object {}} renderAdditionalChildInLine={[Function]} diff --git a/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerCode.tsx b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerCode.tsx index dead6b916ac..c591c404cde 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerCode.tsx +++ b/server/sonar-web/src/main/js/components/SourceViewer/SourceViewerCode.tsx @@ -30,12 +30,9 @@ import { SourceLine } from '../../types/types'; import Line from './components/Line'; +import LineIssuesList from './components/LineIssuesList'; import { getSecondaryIssueLocationsForLine } from './helpers/issueLocations'; -import { - optimizeHighlightedSymbols, - optimizeLocationMessage, - optimizeSelectedIssue -} from './helpers/lines'; +import { optimizeHighlightedSymbols, optimizeLocationMessage } from './helpers/lines'; const EMPTY_ARRAY: any[] = []; @@ -124,9 +121,12 @@ export default class SourceViewerCode extends React.PureComponent<Props> { }) => { const { highlightedLocationMessage, + selectedIssue, + openIssuesByLine, + issueLocationsByLine, + displayAllIssues, highlightedLocations, metricKey, - selectedIssue, sources } = this.props; @@ -152,12 +152,9 @@ export default class SourceViewerCode extends React.PureComponent<Props> { return ( <Line - branchLike={this.props.branchLike} displayAllIssues={this.props.displayAllIssues} displayCoverage={displayCoverage} displayDuplications={displayDuplications} - displayIssueLocationsCount={this.props.displayIssueLocationsCount} - displayIssueLocationsLink={this.props.displayIssueLocationsLink} displayIssues={displayIssues} displayLocationMarkers={this.props.displayLocationMarkers} displaySCM={sources.length > 0} @@ -174,14 +171,11 @@ export default class SourceViewerCode extends React.PureComponent<Props> { this.props.highlightedSymbols )} issueLocations={this.getIssueLocationsForLine(line)} - issuePopup={this.props.issuePopup} issues={issuesForLine} key={line.line || line.code} last={index === this.props.sources.length - 1 && !this.props.hasSourcesAfter} line={line} loadDuplications={this.props.loadDuplications} - onIssueChange={this.props.onIssueChange} - onIssuePopupToggle={this.props.onIssuePopupToggle} onIssueSelect={this.props.onIssueSelect} onIssueUnselect={this.props.onIssueUnselect} onIssuesClose={this.props.onIssuesClose} @@ -193,9 +187,23 @@ export default class SourceViewerCode extends React.PureComponent<Props> { renderDuplicationPopup={this.props.renderDuplicationPopup} scroll={this.props.scroll} scrollToUncoveredLine={scrollToUncoveredLine} - secondaryIssueLocations={secondaryIssueLocations} - selectedIssue={optimizeSelectedIssue(selectedIssue, issuesForLine)} - /> + secondaryIssueLocations={secondaryIssueLocations}> + <LineIssuesList + displayAllIssues={displayAllIssues} + issueLocationsByLine={issueLocationsByLine} + issuesForLine={issuesForLine} + line={line} + openIssuesByLine={openIssuesByLine} + branchLike={this.props.branchLike} + displayIssueLocationsCount={this.props.displayIssueLocationsCount} + displayIssueLocationsLink={this.props.displayIssueLocationsLink} + issuePopup={this.props.issuePopup} + onIssueChange={this.props.onIssueChange} + onIssueClick={this.props.onIssueSelect} + onIssuePopupToggle={this.props.onIssuePopupToggle} + selectedIssue={selectedIssue} + /> + </Line> ); }; diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/Line.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/Line.tsx index 29df74831bd..e8a060ba501 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/Line.tsx +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/Line.tsx @@ -20,7 +20,6 @@ import classNames from 'classnames'; import { times } from 'lodash'; import * as React from 'react'; -import { BranchLike } from '../../../types/branch-like'; import { Issue, LinearIssueLocation, SourceLine } from '../../../types/types'; import './Line.css'; import LineCode from './LineCode'; @@ -31,13 +30,10 @@ import LineNumber from './LineNumber'; import LineSCM from './LineSCM'; interface Props { - additionalChild?: React.ReactNode; - branchLike: BranchLike | undefined; + children?: React.ReactNode; displayAllIssues?: boolean; displayCoverage: boolean; displayDuplications: boolean; - displayIssueLocationsCount?: boolean; - displayIssueLocationsLink?: boolean; displayIssues: boolean; displayLineNumberOptions?: boolean; displayLocationMarkers?: boolean; @@ -49,13 +45,10 @@ interface Props { highlightedLocationMessage: { index: number; text: string | undefined } | undefined; highlightedSymbols: string[] | undefined; issueLocations: LinearIssueLocation[]; - issuePopup: { issue: string; name: string } | undefined; issues: Issue[]; last: boolean; line: SourceLine; loadDuplications: (line: SourceLine) => void; - onIssueChange: (issue: Issue) => void; - onIssuePopupToggle: (issueKey: string, popupName: string, open?: boolean) => void; onIssuesClose: (line: SourceLine) => void; onIssueSelect: (issueKey: string) => void; onIssuesOpen: (line: SourceLine) => void; @@ -68,7 +61,6 @@ interface Props { scroll?: (element: HTMLElement) => void; scrollToUncoveredLine?: boolean; secondaryIssueLocations: LinearIssueLocation[]; - selectedIssue: string | undefined; verticalBuffer?: number; } @@ -91,13 +83,10 @@ export default class Line extends React.PureComponent<Props> { render() { const { - additionalChild, - branchLike, + children, displayAllIssues, displayCoverage, displayDuplications, - displayIssueLocationsCount, - displayIssueLocationsLink, displayLineNumberOptions, displayLocationMarkers, highlightedLocationMessage, @@ -109,7 +98,6 @@ export default class Line extends React.PureComponent<Props> { highlighted, highlightedSymbols, issueLocations, - issuePopup, issues, last, line, @@ -117,7 +105,6 @@ export default class Line extends React.PureComponent<Props> { previousLine, scrollToUncoveredLine, secondaryIssueLocations, - selectedIssue, verticalBuffer } = this.props; @@ -186,28 +173,18 @@ export default class Line extends React.PureComponent<Props> { )} <LineCode - additionalChild={additionalChild} - branchLike={branchLike} - displayIssueLocationsCount={displayIssueLocationsCount} - displayIssueLocationsLink={displayIssueLocationsLink} displayLocationMarkers={displayLocationMarkers} highlightedLocationMessage={highlightedLocationMessage} highlightedSymbols={highlightedSymbols} issueLocations={issueLocations} - issuePopup={issuePopup} - issues={issues} line={line} - onIssueChange={this.props.onIssueChange} - onIssuePopupToggle={this.props.onIssuePopupToggle} - onIssueSelect={this.props.onIssueSelect} onLocationSelect={this.props.onLocationSelect} onSymbolClick={this.props.onSymbolClick} padding={bottomPadding} scroll={this.props.scroll} - secondaryIssueLocations={secondaryIssueLocations} - selectedIssue={selectedIssue} - showIssues={openIssues || displayAllIssues} - /> + secondaryIssueLocations={secondaryIssueLocations}> + {children} + </LineCode> </tr> ); } 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 a6dc9efcd28..21736e0cdff 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 @@ -19,8 +19,7 @@ */ import classNames from 'classnames'; import * as React from 'react'; -import { BranchLike } from '../../../types/branch-like'; -import { Issue, LinearIssueLocation, SourceLine } from '../../../types/types'; +import { LinearIssueLocation, SourceLine } from '../../../types/types'; import LocationIndex from '../../common/LocationIndex'; import Tooltip from '../../controls/Tooltip'; import { @@ -29,37 +28,26 @@ import { splitByTokens, Token } from '../helpers/highlight'; -import LineIssuesList from './LineIssuesList'; interface Props { - additionalChild?: React.ReactNode; - branchLike: BranchLike | undefined; - displayIssueLocationsCount?: boolean; - displayIssueLocationsLink?: boolean; + className?: string; displayLocationMarkers?: boolean; highlightedLocationMessage: { index: number; text: string | undefined } | undefined; highlightedSymbols: string[] | undefined; issueLocations: LinearIssueLocation[]; - issuePopup: { issue: string; name: string } | undefined; - issues: Issue[]; line: SourceLine; - onIssueChange: (issue: Issue) => void; - onIssuePopupToggle: (issue: string, popupName: string, open?: boolean) => void; - onIssueSelect: (issueKey: string) => void; onLocationSelect: ((index: number) => void) | undefined; onSymbolClick: (symbols: Array<string>) => void; padding?: number; scroll?: (element: HTMLElement) => void; secondaryIssueLocations: LinearIssueLocation[]; - selectedIssue: string | undefined; - showIssues?: boolean; } interface State { tokens: Token[]; } -export default class LineCode extends React.PureComponent<Props, State> { +export default class LineCode extends React.PureComponent<React.PropsWithChildren<Props>, State> { activeMarkerNode?: HTMLElement | null; codeNode?: HTMLElement | null; symbols?: NodeListOf<HTMLElement>; @@ -157,17 +145,14 @@ export default class LineCode extends React.PureComponent<Props, State> { render() { const { - additionalChild, + children, + className, highlightedLocationMessage, highlightedSymbols, - issues, issueLocations, line, - onIssueSelect, padding, - secondaryIssueLocations, - selectedIssue, - showIssues + secondaryIssueLocations } = this.props; let tokens = [...this.state.tokens]; @@ -195,10 +180,6 @@ export default class LineCode extends React.PureComponent<Props, State> { } } - const className = classNames('source-line-code', 'code', { - 'has-issues': issues.length > 0 - }); - const renderedTokens: React.ReactNode[] = []; // track if the first marker is displayed before the source code @@ -226,38 +207,17 @@ export default class LineCode extends React.PureComponent<Props, State> { }); const style = padding ? { paddingBottom: `${padding}px` } : undefined; - const filteredSelectedIssues = issues.filter(i => i.key === selectedIssue); return ( - <td className={className} data-line-number={line.line} style={style}> + <td + className={classNames('source-line-code code', className)} + data-line-number={line.line} + style={style}> <div className="source-line-code-inner"> <pre ref={node => (this.codeNode = node)}>{renderedTokens}</pre> </div> - {showIssues && issues.length > 0 && ( - <LineIssuesList - branchLike={this.props.branchLike} - displayIssueLocationsCount={this.props.displayIssueLocationsCount} - displayIssueLocationsLink={this.props.displayIssueLocationsLink} - issuePopup={this.props.issuePopup} - issues={issues} - onIssueChange={this.props.onIssueChange} - onIssueClick={onIssueSelect} - onIssuePopupToggle={this.props.onIssuePopupToggle} - selectedIssue={selectedIssue} - /> - )} - {selectedIssue && !showIssues && issueLocations.length > 0 && ( - <LineIssuesList - branchLike={this.props.branchLike} - issuePopup={this.props.issuePopup} - issues={filteredSelectedIssues} - onIssueChange={this.props.onIssueChange} - onIssueClick={onIssueSelect} - onIssuePopupToggle={this.props.onIssuePopupToggle} - selectedIssue={selectedIssue} - /> - )} - {additionalChild} + + {children} </td> ); } diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/LineIssuesList.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/LineIssuesList.tsx index dbb0aca4ac4..5584d032e47 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/LineIssuesList.tsx +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/LineIssuesList.tsx @@ -19,27 +19,51 @@ */ import * as React from 'react'; import { BranchLike } from '../../../types/branch-like'; -import { Issue as TypeIssue } from '../../../types/types'; +import { Issue as TypeIssue, LinearIssueLocation, SourceLine } from '../../../types/types'; import Issue from '../../issue/Issue'; -interface Props { +export interface LineIssuesListProps { branchLike: BranchLike | undefined; + displayAllIssues?: boolean; displayIssueLocationsCount?: boolean; displayIssueLocationsLink?: boolean; + issuesForLine: TypeIssue[]; issuePopup: { issue: string; name: string } | undefined; - issues: TypeIssue[]; + issueLocationsByLine: { [line: number]: LinearIssueLocation[] }; + line: SourceLine; onIssueChange: (issue: TypeIssue) => void; onIssueClick: (issueKey: string) => void; onIssuePopupToggle: (issue: string, popupName: string, open?: boolean) => void; + openIssuesByLine: { [line: number]: boolean }; selectedIssue: string | undefined; } -export default function LineIssuesList(props: Props) { - const { issuePopup } = props; +export default function LineIssuesList(props: LineIssuesListProps) { + const { + line, + displayAllIssues, + openIssuesByLine, + selectedIssue, + issuesForLine, + issueLocationsByLine, + issuePopup + } = props; + const showIssues = openIssuesByLine[line.line] || displayAllIssues; + const issueLocations = issueLocationsByLine[line.line] || []; + let displayedIssue: TypeIssue[] = []; + if (showIssues && issuesForLine.length > 0) { + displayedIssue = issuesForLine; + } else if (selectedIssue && !showIssues && issueLocations.length) { + displayedIssue = issuesForLine.filter(i => i.key === selectedIssue); + } + + if (displayedIssue.length === 0) { + return null; + } return ( <div className="issue-list"> - {props.issues.map(issue => ( + {displayedIssue.map(issue => ( <Issue branchLike={props.branchLike} displayLocationsCount={props.displayIssueLocationsCount} diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/Line-test.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/Line-test.tsx index e16954906d8..cb3b97075cb 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/Line-test.tsx +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/Line-test.tsx @@ -19,7 +19,6 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; -import { mockPullRequest } from '../../../../helpers/mocks/branch-like'; import { mockIssue, mockSourceLine } from '../../../../helpers/testMocks'; import Line from '../Line'; @@ -64,7 +63,6 @@ it('handles the opening and closing of issues', () => { function shallowRender(props: Partial<Line['props']> = {}) { return shallow<Line>( <Line - branchLike={mockPullRequest()} displayAllIssues={false} displayCoverage={false} displayDuplications={false} @@ -77,13 +75,10 @@ function shallowRender(props: Partial<Line['props']> = {}) { highlightedLocationMessage={undefined} highlightedSymbols={undefined} issueLocations={[]} - issuePopup={undefined} issues={[mockIssue(), mockIssue(false, { type: 'VULNERABILITY' })]} last={false} line={mockSourceLine()} loadDuplications={jest.fn()} - onIssueChange={jest.fn()} - onIssuePopupToggle={jest.fn()} onIssuesClose={jest.fn()} onIssueSelect={jest.fn()} onIssuesOpen={jest.fn()} @@ -95,7 +90,6 @@ function shallowRender(props: Partial<Line['props']> = {}) { renderDuplicationPopup={jest.fn()} scroll={jest.fn()} secondaryIssueLocations={[]} - selectedIssue={undefined} {...props} /> ); 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 616878e5037..8d15db4f708 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 @@ -19,14 +19,12 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; -import { mockBranch } from '../../../../helpers/mocks/branch-like'; -import { mockIssue, mockSourceLine } from '../../../../helpers/testMocks'; +import { mockSourceLine } from '../../../../helpers/testMocks'; import LineCode from '../LineCode'; -import LineIssuesList from '../LineIssuesList'; it('render code', () => { expect(shallowRender()).toMatchSnapshot(); - expect(shallowRender({ additionalChild: <div>additional child</div> })).toMatchSnapshot( + expect(shallowRender({ children: <div>additional child</div> })).toMatchSnapshot( 'with additional child' ); expect( @@ -38,31 +36,17 @@ it('render code', () => { ).toMatchSnapshot('with secondary location'); }); -it('should not render issue list when no issue location', () => { - const wrapper = shallowRender({ issueLocations: [], showIssues: false }); - - expect(wrapper.find(LineIssuesList).length).toBe(0); -}); - function shallowRender(props: Partial<LineCode['props']> = {}) { return shallow( <LineCode - branchLike={mockBranch()} displayLocationMarkers={true} highlightedLocationMessage={{ index: 0, text: 'location description' }} highlightedSymbols={['sym-9']} issueLocations={[{ from: 0, to: 5, line: 16 }]} - issuePopup={undefined} - issues={[mockIssue(false, { key: 'issue-1' }), mockIssue(false, { key: 'issue-2' })]} line={mockSourceLine()} - onIssueChange={jest.fn()} - onIssuePopupToggle={jest.fn()} - onIssueSelect={jest.fn()} onLocationSelect={jest.fn()} onSymbolClick={jest.fn()} secondaryIssueLocations={[]} - selectedIssue="issue-1" - showIssues={true} {...props} /> ); diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineIssueList-test.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineIssueList-test.tsx new file mode 100644 index 00000000000..70ec54f61d2 --- /dev/null +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineIssueList-test.tsx @@ -0,0 +1,52 @@ +/* + * SonarQube + * Copyright (C) 2009-2022 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { shallow } from 'enzyme'; +import * as React from 'react'; +import { mockBranch } from '../../../../helpers/mocks/branch-like'; +import { mockIssue, mockSourceLine } from '../../../../helpers/testMocks'; +import LineIssuesList, { LineIssuesListProps } from '../LineIssuesList'; + +it('shoule render issues', () => { + const wrapper = shallowRender({ + selectedIssue: 'issue', + issueLocationsByLine: { '1': [{ from: 1, to: 1, line: 1 }] }, + line: mockSourceLine({ line: 1 }), + issuesForLine: [mockIssue(false, { key: 'issue' })] + }); + expect(wrapper).toMatchSnapshot(); +}); + +function shallowRender(props: Partial<LineIssuesListProps> = {}) { + return shallow( + <LineIssuesList + selectedIssue="" + onIssueChange={jest.fn()} + onIssueClick={jest.fn()} + onIssuePopupToggle={jest.fn()} + openIssuesByLine={{}} + branchLike={mockBranch()} + issueLocationsByLine={{}} + line={mockSourceLine()} + issuePopup={undefined} + issuesForLine={[]} + {...props} + /> + ); +} diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/Line-test.tsx.snap b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/Line-test.tsx.snap index e916b9827db..d848cd62fe0 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/Line-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/Line-test.tsx.snap @@ -41,80 +41,8 @@ exports[`should render correctly for last, new, and highlighted lines 1`] = ` className="source-meta source-line-issues" /> <LineCode - branchLike={ - Object { - "analysisDate": "2018-01-01", - "base": "master", - "branch": "feature/foo/bar", - "key": "1001", - "target": "master", - "title": "Foo Bar feature", - } - } displayLocationMarkers={false} issueLocations={Array []} - 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": "AVsae-CQS-9G3txfbFN2", - "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": "AVsae-CQS-9G3txfbFN2", - "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": "VULNERABILITY", - }, - ] - } line={ Object { "code": "<span class=\\"k\\">import</span> java.util.<span class=\\"sym-9 sym\\">ArrayList</span>;", @@ -128,14 +56,10 @@ exports[`should render correctly for last, new, and highlighted lines 1`] = ` "scmRevision": "80f564becc0c0a1c9abaa006eca83a4fd278c3f0", } } - onIssueChange={[MockFunction]} - onIssuePopupToggle={[MockFunction]} - onIssueSelect={[MockFunction]} onLocationSelect={[MockFunction]} onSymbolClick={[MockFunction]} scroll={[MockFunction]} secondaryIssueLocations={Array []} - showIssues={false} /> </tr> `; 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 db64b67a1dc..fa518fddd0e 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 @@ -2,7 +2,7 @@ exports[`render code 1`] = ` <td - className="source-line-code code has-issues" + className="source-line-code code" data-line-number={16} > <div @@ -41,88 +41,12 @@ exports[`render code 1`] = ` </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> `; exports[`render code: with additional child 1`] = ` <td - className="source-line-code code has-issues" + className="source-line-code code" data-line-number={16} > <div @@ -161,82 +85,6 @@ exports[`render code: with additional child 1`] = ` </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" - /> <div> additional child </div> @@ -245,7 +93,7 @@ exports[`render code: with additional child 1`] = ` exports[`render code: with secondary location 1`] = ` <td - className="source-line-code code has-issues" + className="source-line-code code" data-line-number={16} > <div @@ -300,81 +148,5 @@ exports[`render code: with secondary location 1`] = ` </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/components/SourceViewer/components/__tests__/__snapshots__/LineIssueList-test.tsx.snap b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineIssueList-test.tsx.snap new file mode 100644 index 00000000000..eef65511cda --- /dev/null +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/__snapshots__/LineIssueList-test.tsx.snap @@ -0,0 +1,54 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`shoule render issues 1`] = ` +<div + className="issue-list" +> + <Issue + branchLike={ + Object { + "analysisDate": "2018-01-01", + "excludedFromPurge": true, + "isMain": false, + "name": "branch-6.7", + } + } + issue={ + 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", + "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", + } + } + key="issue" + onChange={[MockFunction]} + onClick={[MockFunction]} + onPopupToggle={[MockFunction]} + selected={true} + /> +</div> +`; diff --git a/server/sonar-web/src/main/js/components/SourceViewer/helpers/lines.ts b/server/sonar-web/src/main/js/components/SourceViewer/helpers/lines.ts index ac520aca377..ef4bcf0348c 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/helpers/lines.ts +++ b/server/sonar-web/src/main/js/components/SourceViewer/helpers/lines.ts @@ -18,7 +18,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { intersection } from 'lodash'; -import { Issue, LinearIssueLocation } from '../../../types/types'; +import { LinearIssueLocation } from '../../../types/types'; export const LINES_TO_LOAD = 500; @@ -42,9 +42,3 @@ export function optimizeLocationMessage( ? highlightedLocationMessage : undefined; } - -export function optimizeSelectedIssue(selectedIssue: string | undefined, issuesForLine: Issue[]) { - return selectedIssue !== undefined && issuesForLine.find(issue => issue.key === selectedIssue) - ? selectedIssue - : undefined; -} |