From 19c1858b5e72a19332e6a77fd532712e07b47a77 Mon Sep 17 00:00:00 2001 From: Jeremy Davis Date: Tue, 1 Dec 2020 14:39:15 +0100 Subject: [PATCH] SONAR-14130 Always underline Primary Location in snippets --- .../ComponentSourceSnippetGroupViewer.tsx | 7 +++--- .../CrossComponentSourceViewerWrapper.tsx | 7 ++++++ ...ComponentSourceSnippetGroupViewer-test.tsx | 22 +++++++++++++++++++ ...ComponentSourceViewerWrapper-test.tsx.snap | 1 + 4 files changed, 34 insertions(+), 3 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 c70d98dd813..d007ec132b4 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 @@ -40,6 +40,7 @@ interface Props { duplications?: T.Duplication[]; duplicationsByLine?: { [line: number]: number[] }; highlightedLocationMessage: { index: number; text: string | undefined } | undefined; + isLastOccurenceOfPrimaryComponent: boolean; issue: T.Issue; issuePopup?: { issue: string; name: string }; issuesByLine: T.IssuesByLine; @@ -348,6 +349,7 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone render() { const { branchLike, + isLastOccurenceOfPrimaryComponent, issue, issuesByLine, issuePopup, @@ -372,8 +374,7 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone }); const isFlow = issue.secondaryLocations.length === 0; - const includeIssueLocation = (snippetIndex: number) => - isFlow ? lastSnippetGroup && snippetIndex === snippets.length - 1 : snippetIndex === 0; + const includeIssueLocation = isFlow ? isLastOccurenceOfPrimaryComponent : true; return (
@@ -402,7 +403,7 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone snippet, index: snippets[index].index, issuesByLine, - locationsByLine: includeIssueLocation(index) ? locations : {}, + locationsByLine: includeIssueLocation ? locations : {}, lastSnippetOfLastGroup: lastSnippetGroup && index === snippets.length - 1 })}
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewerWrapper.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewerWrapper.tsx index be54678e2ff..f97ecebc951 100644 --- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewerWrapper.tsx +++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewerWrapper.tsx @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { findLastIndex } from 'lodash'; import * as React from 'react'; import { Alert } from 'sonar-ui-common/components/ui/Alert'; import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; @@ -194,6 +195,11 @@ export default class CrossComponentSourceViewerWrapper extends React.PureCompone const issuesByComponent = issuesByComponentAndLine(this.props.issues); const locationsByComponent = groupLocationsByComponent(issue, locations, components); + const lastOccurenceOfPrimaryComponent = findLastIndex( + locationsByComponent, + ({ component }) => component.key === issue.component + ); + return (
{locationsByComponent.map((snippetGroup, i) => { @@ -210,6 +216,7 @@ export default class CrossComponentSourceViewerWrapper extends React.PureCompone issue={issue} issuePopup={this.state.issuePopup} issuesByLine={issuesByComponent[snippetGroup.component.key] || {}} + isLastOccurenceOfPrimaryComponent={i === lastOccurenceOfPrimaryComponent} lastSnippetGroup={i === locationsByComponent.length - 1} loadDuplications={this.fetchDuplications} locations={snippetGroup.locations || []} 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 7cbb22843f9..fe373363117 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 @@ -32,6 +32,7 @@ import { mockSourceViewerFile } from '../../../../helpers/testMocks'; import ComponentSourceSnippetGroupViewer from '../ComponentSourceSnippetGroupViewer'; +import SnippetViewer from '../SnippetViewer'; jest.mock('../../../../api/components', () => ({ getSources: jest.fn().mockResolvedValue([]) @@ -104,6 +105,24 @@ it('should render correctly with flows', () => { expect(wrapper.state('snippets')).toHaveLength(2); expect(wrapper.state('snippets')[0]).toEqual({ index: 0, start: 29, end: 39 }); expect(wrapper.state('snippets')[1]).toEqual({ index: 1, start: 69, end: 79 }); + + // Check that locationsByLine is defined when isLastOccurenceOfPrimaryComponent + expect( + wrapper + .find(SnippetViewer) + .at(0) + .props().locationsByLine + ).not.toEqual({}); + + // If not, it should be an empty object: + const snippets = shallowRender({ + isLastOccurenceOfPrimaryComponent: false, + issue, + snippetGroup + }).find(SnippetViewer); + + expect(snippets.at(0).props().locationsByLine).toEqual({}); + expect(snippets.at(1).props().locationsByLine).toEqual({}); }); it('should render file-level issue correctly', () => { @@ -281,6 +300,7 @@ describe('getNodes', () => { {