aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer
diff options
context:
space:
mode:
Diffstat (limited to 'server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer')
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/ComponentSourceSnippetGroupViewer.tsx235
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewer.tsx45
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.css11
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx5
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.css7
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.tsx47
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/ComponentSourceSnippetGroupViewer-test.tsx179
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/CrossComponentSourceViewer-test.tsx27
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/SnippetViewer-test.tsx52
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/ComponentSourceSnippetGroupViewer-test.tsx.snap6
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/CrossComponentSourceViewer-test.tsx.snap129
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/IssueSourceViewerHeader-test.tsx.snap16
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/SnippetViewer-test.tsx.snap17
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/utils-test.ts2
-rw-r--r--server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/utils.ts4
15 files changed, 136 insertions, 646 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 08a9d12d6aa..bbdff07c952 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
@@ -33,7 +33,6 @@ import {
FlowLocation,
Issue as TypeIssue,
IssuesByLine,
- LinearIssueLocation,
Snippet,
SnippetGroup,
SourceLine,
@@ -45,6 +44,7 @@ import {
createSnippets,
expandSnippet,
EXPAND_BY_LINES,
+ getPrimaryLocation,
linesForSnippets,
MERGE_DISTANCE
} from './utils';
@@ -70,7 +70,6 @@ interface Props {
index: number,
line: number
) => React.ReactNode;
- scroll?: (element: HTMLElement, offset: number) => void;
snippetGroup: SnippetGroup;
}
@@ -83,13 +82,16 @@ interface State {
export default class ComponentSourceSnippetGroupViewer extends React.PureComponent<Props, State> {
mounted = false;
- rootNodeRef = React.createRef<HTMLDivElement>();
- state: State = {
- additionalLines: {},
- highlightedSymbols: [],
- loading: false,
- snippets: []
- };
+
+ constructor(props: Props) {
+ super(props);
+ this.state = {
+ additionalLines: {},
+ highlightedSymbols: [],
+ loading: false,
+ snippets: []
+ };
+ }
componentDidMount() {
this.mounted = true;
@@ -106,76 +108,13 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone
const snippets = createSnippets({
component: snippetGroup.component.key,
issue,
- locations: snippetGroup.locations
+ locations:
+ snippetGroup.locations.length === 0 ? [getPrimaryLocation(issue)] : snippetGroup.locations
});
this.setState({ snippets });
}
- getNodes(index: number): { wrapper: HTMLElement; table: HTMLElement } | undefined {
- const root = this.rootNodeRef.current;
- if (!root) {
- return undefined;
- }
- const element = root.querySelector(`#snippet-wrapper-${index}`);
- if (!element) {
- return undefined;
- }
- const wrapper = element.querySelector<HTMLElement>('.snippet');
- if (!wrapper) {
- return undefined;
- }
- const table = wrapper.firstChild as HTMLElement;
- if (!table) {
- return undefined;
- }
-
- return { wrapper, table };
- }
-
- /*
- * Clean after animation
- */
- cleanDom(index: number) {
- const nodes = this.getNodes(index);
-
- if (!nodes) {
- return;
- }
-
- const { wrapper, table } = nodes;
-
- table.style.marginTop = '';
- wrapper.style.maxHeight = '';
- }
-
- setMaxHeight(index: number, value?: number, up = false) {
- const nodes = this.getNodes(index);
-
- if (!nodes) {
- return;
- }
-
- const { wrapper, table } = nodes;
-
- const maxHeight = value !== undefined ? value : table.getBoundingClientRect().height;
-
- if (up) {
- const startHeight = wrapper.getBoundingClientRect().height;
- table.style.transition = 'none';
- table.style.marginTop = `${startHeight - maxHeight}px`;
-
- // animate!
- setTimeout(() => {
- table.style.transition = '';
- table.style.marginTop = '0px';
- wrapper.style.maxHeight = `${maxHeight + 20}px`;
- }, 0);
- } else {
- wrapper.style.maxHeight = `${maxHeight + 20}px`;
- }
- }
-
expandBlock = (snippetIndex: number, direction: ExpandDirection): Promise<void> => {
const { branchLike, snippetGroup } = this.props;
const { key } = snippetGroup.component;
@@ -208,56 +147,22 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone
return lineMap;
}, {})
)
- .then(newLinesMapped => this.animateBlockExpansion(snippetIndex, direction, newLinesMapped));
- };
-
- animateBlockExpansion(
- snippetIndex: number,
- direction: ExpandDirection,
- newLinesMapped: Dict<SourceLine>
- ): Promise<void> {
- if (this.mounted) {
- const { snippets } = this.state;
-
- const newSnippets = expandSnippet({
- direction,
- snippetIndex,
- snippets
- });
-
- const deletedSnippets = newSnippets.filter(s => s.toDelete);
-
- // set max-height to current height for CSS transitions
- deletedSnippets.forEach(s => this.setMaxHeight(s.index));
- this.setMaxHeight(snippetIndex);
-
- return new Promise(resolve => {
- this.setState(
- ({ additionalLines, snippets }) => {
- const combinedLines = { ...additionalLines, ...newLinesMapped };
- return {
- additionalLines: combinedLines,
- snippets
- };
- },
- () => {
- // Set max-height 0 to trigger CSS transitions
- deletedSnippets.forEach(s => {
- this.setMaxHeight(s.index, 0);
- });
- this.setMaxHeight(snippetIndex, undefined, direction === 'up');
-
- // Wait for transition to finish before updating dom
- setTimeout(() => {
- this.setState({ snippets: newSnippets.filter(s => !s.toDelete) }, resolve);
- this.cleanDom(snippetIndex);
- }, 200);
- }
- );
+ .then(newLinesMapped => {
+ const newSnippets = expandSnippet({
+ direction,
+ snippetIndex,
+ snippets
+ });
+
+ this.setState(({ additionalLines }) => {
+ const combinedLines = { ...additionalLines, ...newLinesMapped };
+ return {
+ additionalLines: combinedLines,
+ snippets: newSnippets.filter(s => !s.toDelete)
+ };
+ });
});
- }
- return Promise.resolve();
- }
+ };
expandComponent = () => {
const { branchLike, snippetGroup } = this.props;
@@ -356,41 +261,6 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone
);
};
- renderSnippet({
- index,
- lastSnippetOfLastGroup,
- locationsByLine,
- snippet
- }: {
- index: number;
- lastSnippetOfLastGroup: boolean;
- locationsByLine: { [line: number]: LinearIssueLocation[] };
- snippet: SourceLine[];
- }) {
- return (
- <SnippetViewer
- renderAdditionalChildInLine={this.renderIssuesList}
- component={this.props.snippetGroup.component}
- duplications={this.props.duplications}
- duplicationsByLine={this.props.duplicationsByLine}
- expandBlock={this.expandBlock}
- handleSymbolClick={this.handleSymbolClick}
- highlightedLocationMessage={this.props.highlightedLocationMessage}
- highlightedSymbols={this.state.highlightedSymbols}
- index={index}
- issue={this.props.issue}
- lastSnippetOfLastGroup={lastSnippetOfLastGroup}
- loadDuplications={this.loadDuplications}
- locations={this.props.locations}
- locationsByLine={locationsByLine}
- onLocationSelect={this.props.onLocationSelect}
- renderDuplicationPopup={this.renderDuplicationPopup}
- scroll={this.props.scroll}
- snippet={snippet}
- />
- );
- }
-
render() {
const {
branchLike,
@@ -421,7 +291,7 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone
const includeIssueLocation = isFlow ? isLastOccurenceOfPrimaryComponent : true;
return (
- <div className="component-source-container" ref={this.rootNodeRef}>
+ <>
<IssueSourceViewerHeader
branchLike={branchLike}
expandable={!fullyShown && isFile(snippetGroup.component.q)}
@@ -429,28 +299,39 @@ export default class ComponentSourceSnippetGroupViewer extends React.PureCompone
onExpand={this.expandComponent}
sourceViewerFile={snippetGroup.component}
/>
+
{issue.component === snippetGroup.component.key && issue.textRange === undefined && (
- <div className="padded-top padded-left padded-right">
- <Issue
- issue={issue}
- onChange={this.props.onIssueChange}
- onPopupToggle={this.props.onIssuePopupToggle}
- openPopup={issuePopup && issuePopup.issue === issue.key ? issuePopup.name : undefined}
- selected={true}
- />
- </div>
+ <Issue
+ issue={issue}
+ onChange={this.props.onIssueChange}
+ onPopupToggle={this.props.onIssuePopupToggle}
+ openPopup={issuePopup && issuePopup.issue === issue.key ? issuePopup.name : undefined}
+ selected={true}
+ />
)}
{snippetLines.map((snippet, index) => (
- <div id={`snippet-wrapper-${snippets[index].index}`} key={snippets[index].index}>
- {this.renderSnippet({
- snippet,
- index: snippets[index].index,
- locationsByLine: includeIssueLocation ? locations : {},
- lastSnippetOfLastGroup: lastSnippetGroup && index === snippets.length - 1
- })}
- </div>
+ <SnippetViewer
+ key={snippets[index].index}
+ renderAdditionalChildInLine={this.renderIssuesList}
+ component={this.props.snippetGroup.component}
+ duplications={this.props.duplications}
+ duplicationsByLine={this.props.duplicationsByLine}
+ expandBlock={this.expandBlock}
+ handleSymbolClick={this.handleSymbolClick}
+ highlightedLocationMessage={this.props.highlightedLocationMessage}
+ highlightedSymbols={this.state.highlightedSymbols}
+ index={index}
+ issue={this.props.issue}
+ lastSnippetOfLastGroup={lastSnippetGroup && index === snippets.length - 1}
+ loadDuplications={this.loadDuplications}
+ locations={this.props.locations}
+ locationsByLine={includeIssueLocation ? locations : {}}
+ onLocationSelect={this.props.onLocationSelect}
+ renderDuplicationPopup={this.renderDuplicationPopup}
+ snippet={snippet}
+ />
))}
- </div>
+ </>
);
}
}
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewer.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewer.tsx
index 0457ce290df..85ab58c3812 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewer.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewer.tsx
@@ -51,7 +51,7 @@ import {
SourceViewerFile
} from '../../../types/types';
import ComponentSourceSnippetGroupViewer from './ComponentSourceSnippetGroupViewer';
-import { getPrimaryLocation, groupLocationsByComponent } from './utils';
+import { groupLocationsByComponent } from './utils';
interface Props {
branchLike: BranchLike | undefined;
@@ -63,7 +63,6 @@ interface Props {
onIssueSelect: (issueKey: string) => void;
onLoaded?: () => void;
onLocationSelect: (index: number) => void;
- scroll?: (element: HTMLElement) => void;
selectedFlowIndex: number | undefined;
}
@@ -226,9 +225,8 @@ export default class CrossComponentSourceViewer extends React.PureComponent<Prop
const issuesByComponent = issuesByComponentAndLine(this.props.issues);
const locationsByComponent = groupLocationsByComponent(issue, locations, components);
- const lastOccurenceOfPrimaryComponent = findLastIndex(
- locationsByComponent,
- ({ component }) => component.key === issue.component
+ const lastOccurenceOfPrimaryComponent = findLastIndex(locationsByComponent, ({ component }) =>
+ component ? component.key === issue.component : true
);
if (components[issue.component] === undefined) {
@@ -236,7 +234,7 @@ export default class CrossComponentSourceViewer extends React.PureComponent<Prop
}
return (
- <div>
+ <>
{locationsByComponent.map((snippetGroup, i) => {
return (
<SourceViewerContext.Provider
@@ -260,45 +258,12 @@ export default class CrossComponentSourceViewer extends React.PureComponent<Prop
onIssuePopupToggle={this.handleIssuePopupToggle}
onLocationSelect={this.props.onLocationSelect}
renderDuplicationPopup={this.renderDuplicationPopup}
- scroll={this.props.scroll}
snippetGroup={snippetGroup}
/>
</SourceViewerContext.Provider>
);
})}
-
- {locationsByComponent.length === 0 && (
- <SourceViewerContext.Provider
- value={{
- branchLike: this.props.branchLike,
- file: components[issue.component].component
- }}>
- <ComponentSourceSnippetGroupViewer
- branchLike={this.props.branchLike}
- duplications={duplications}
- duplicationsByLine={duplicationsByLine}
- highlightedLocationMessage={this.props.highlightedLocationMessage}
- issue={issue}
- issuePopup={this.state.issuePopup}
- issuesByLine={issuesByComponent[issue.component] || {}}
- isLastOccurenceOfPrimaryComponent={true}
- lastSnippetGroup={true}
- loadDuplications={this.fetchDuplications}
- locations={[]}
- onIssueChange={this.props.onIssueChange}
- onIssueSelect={this.props.onIssueSelect}
- onIssuePopupToggle={this.handleIssuePopupToggle}
- onLocationSelect={this.props.onLocationSelect}
- renderDuplicationPopup={this.renderDuplicationPopup}
- scroll={this.props.scroll}
- snippetGroup={{
- locations: [getPrimaryLocation(issue)],
- ...components[issue.component]
- }}
- />
- </SourceViewerContext.Provider>
- )}
- </div>
+ </>
);
}
}
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.css b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.css
index 4f70cddf426..b06083adb3d 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.css
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.css
@@ -17,10 +17,19 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-.source-viewer-header-slim {
+.issue-source-viewer-header {
padding: 4px 10px;
border: 1px solid var(--gray80);
background-color: var(--barBackgroundColor);
align-items: center;
min-height: 25px;
+ position: sticky;
+ z-index: 100;
+ top: 0;
+ margin-top: 8px;
+ margin-bottom: -1px;
+}
+
+.issue-source-viewer-header:first-child {
+ margin-top: 0;
}
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx
index 69b4465cbe1..7bbee7fcc1e 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/IssueSourceViewerHeader.tsx
@@ -65,7 +65,10 @@ export default function IssueSourceViewerHeader(props: Props) {
const isProjectRoot = q === ComponentQualifier.Project;
return (
- <div className="source-viewer-header-slim display-flex-row display-flex-space-between">
+ <div
+ className="issue-source-viewer-header display-flex-row display-flex-space-between"
+ role="separator"
+ aria-label={sourceViewerFile.path}>
<div className="display-flex-center flex-1">
{displayProjectName && (
<div className="spacer-right">
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.css b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.css
index b3b151ce897..69e8cc7756f 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.css
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/SnippetViewer.css
@@ -18,11 +18,12 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
.snippet {
- margin: var(--gridSize) 0;
border: 1px solid var(--gray80);
overflow-x: auto;
- overflow-y: hidden;
- transition: max-height 0.2s;
+}
+
+.snippet + .snippet {
+ margin-top: 8px;
}
.snippet > div {
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 9981db89700..1e21f308b1a 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
@@ -28,7 +28,6 @@ import {
optimizeLocationMessage
} from '../../../components/SourceViewer/helpers/lines';
import { translate } from '../../../helpers/l10n';
-import { scrollHorizontally } from '../../../helpers/scrolling';
import {
Duplication,
ExpandDirection,
@@ -60,53 +59,12 @@ interface Props {
onLocationSelect: (index: number) => void;
renderAdditionalChildInLine?: (line: SourceLine) => React.ReactNode | undefined;
renderDuplicationPopup: (index: number, line: number) => React.ReactNode;
- scroll?: (element: HTMLElement, offset?: number) => void;
snippet: SourceLine[];
}
export default class SnippetViewer extends React.PureComponent<Props> {
- snippetNodeRef: React.RefObject<HTMLDivElement>;
-
- constructor(props: Props) {
- super(props);
- this.snippetNodeRef = React.createRef();
- }
-
- doScroll = (element: HTMLElement) => {
- if (this.props.scroll) {
- this.props.scroll(element);
- }
- const parent = this.snippetNodeRef.current as Element;
-
- if (parent) {
- const offset = parent.getBoundingClientRect().width / 2;
-
- scrollHorizontally(element, {
- leftOffset: offset,
- rightOffset: offset,
- parent
- });
- }
- };
-
- scrollToLastExpandedRow = () => {
- if (this.props.scroll) {
- const snippetNode = this.snippetNodeRef.current as Element;
- if (!snippetNode) {
- return;
- }
- const rows = snippetNode.querySelectorAll('tr');
- const lastRow = rows[rows.length - 1];
- this.props.scroll(lastRow, 100);
- }
- };
-
expandBlock = (direction: ExpandDirection) => () =>
- this.props.expandBlock(this.props.index, direction).then(() => {
- if (direction === 'down') {
- this.scrollToLastExpandedRow();
- }
- });
+ this.props.expandBlock(this.props.index, direction);
renderLine({
displayDuplications,
@@ -169,7 +127,6 @@ export default class SnippetViewer extends React.PureComponent<Props> {
openIssues={false}
previousLine={index > 0 ? snippet[index - 1] : undefined}
renderDuplicationPopup={this.props.renderDuplicationPopup}
- scroll={this.doScroll}
secondaryIssueLocations={secondaryIssueLocations}
verticalBuffer={verticalBuffer}>
{this.props.renderAdditionalChildInLine && this.props.renderAdditionalChildInLine(line)}
@@ -203,7 +160,7 @@ export default class SnippetViewer extends React.PureComponent<Props> {
Boolean(this.props.loadDuplications) && snippet.some(s => !!s.duplicated);
return (
- <div className="source-viewer-code snippet" ref={this.snippetNodeRef}>
+ <div className="source-viewer-code snippet">
<div>
{snippet[0].line > 1 && (
<div className="expand-block expand-block-above">
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 12d34757886..0d92fa6b622 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
@@ -17,7 +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 { mount, ReactWrapper, shallow } from 'enzyme';
+import { shallow } from 'enzyme';
import { range, times } from 'lodash';
import * as React from 'react';
import { getSources } from '../../../../api/components';
@@ -38,23 +38,6 @@ jest.mock('../../../../api/components', () => ({
getSources: jest.fn().mockResolvedValue([])
}));
-/*
- * Quick & dirty fix to make the tests pass
- * this whole thing should be replaced by RTL tests!
- */
-jest.mock('react-router-dom', () => {
- const routerDom = jest.requireActual('react-router-dom');
-
- function Link() {
- return <div>Link</div>;
- }
-
- return {
- ...routerDom,
- Link
- };
-});
-
beforeEach(() => {
jest.clearAllMocks();
});
@@ -301,121 +284,6 @@ it('should correctly handle lines actions', () => {
);
});
-describe('getNodes', () => {
- const snippetGroup: SnippetGroup = {
- component: mockSourceViewerFile(),
- locations: [],
- sources: []
- };
- const wrapper = mount<ComponentSourceSnippetGroupViewer>(
- <ComponentSourceSnippetGroupViewer
- branchLike={mockMainBranch()}
- highlightedLocationMessage={{ index: 0, text: '' }}
- isLastOccurenceOfPrimaryComponent={true}
- issue={mockIssue()}
- issuesByLine={{}}
- lastSnippetGroup={false}
- loadDuplications={jest.fn()}
- locations={[]}
- onIssueChange={jest.fn()}
- onIssueSelect={jest.fn()}
- onIssuePopupToggle={jest.fn()}
- onLocationSelect={jest.fn()}
- renderDuplicationPopup={jest.fn()}
- scroll={jest.fn()}
- snippetGroup={snippetGroup}
- />
- );
-
- it('should return undefined if any node is missing', async () => {
- await waitAndUpdate(wrapper);
- const rootNode = wrapper.instance().rootNodeRef;
- mockDom(rootNode.current!);
- expect(wrapper.instance().getNodes(0)).toBeUndefined();
- expect(wrapper.instance().getNodes(1)).toBeUndefined();
- expect(wrapper.instance().getNodes(2)).toBeUndefined();
- });
-
- it('should return elements if dom is correct', async () => {
- await waitAndUpdate(wrapper);
- const rootNode = wrapper.instance().rootNodeRef;
- mockDom(rootNode.current!);
- expect(wrapper.instance().getNodes(3)).not.toBeUndefined();
- });
-
- it('should enable cleaning the dom', async () => {
- await waitAndUpdate(wrapper);
- const rootNode = wrapper.instance().rootNodeRef;
- mockDom(rootNode.current!);
-
- wrapper.instance().cleanDom(3);
- const nodes = wrapper.instance().getNodes(3);
- expect(nodes!.wrapper.style.maxHeight).toBe('');
- expect(nodes!.table.style.marginTop).toBe('');
- });
-});
-
-describe('getHeight', () => {
- beforeAll(() => {
- jest.useFakeTimers();
- });
-
- afterAll(() => {
- jest.runOnlyPendingTimers();
- jest.useRealTimers();
- });
-
- const snippetGroup: SnippetGroup = {
- component: mockSourceViewerFile(),
- locations: [],
- sources: []
- };
- const wrapper = mount<ComponentSourceSnippetGroupViewer>(
- <ComponentSourceSnippetGroupViewer
- branchLike={mockMainBranch()}
- highlightedLocationMessage={{ index: 0, text: '' }}
- isLastOccurenceOfPrimaryComponent={true}
- issue={mockIssue()}
- issuesByLine={{}}
- lastSnippetGroup={false}
- loadDuplications={jest.fn()}
- locations={[]}
- onIssueChange={jest.fn()}
- onIssueSelect={jest.fn()}
- onIssuePopupToggle={jest.fn()}
- onLocationSelect={jest.fn()}
- renderDuplicationPopup={jest.fn()}
- scroll={jest.fn()}
- snippetGroup={snippetGroup}
- />
- );
-
- it('should set maxHeight to current height', async () => {
- await waitAndUpdate(wrapper);
-
- const nodes = mockDomForSizes(wrapper, { wrapperHeight: 42, tableHeight: 68 });
- wrapper.instance().setMaxHeight(0);
-
- expect(nodes.wrapper.getAttribute('style')).toBe('max-height: 88px;');
- expect(nodes.table.getAttribute('style')).toBeNull();
- });
-
- it('should set margin and then maxHeight for a nice upwards animation', async () => {
- await waitAndUpdate(wrapper);
-
- const nodes = mockDomForSizes(wrapper, { wrapperHeight: 42, tableHeight: 68 });
- wrapper.instance().setMaxHeight(0, undefined, true);
-
- expect(nodes.wrapper.getAttribute('style')).toBeNull();
- expect(nodes.table.getAttribute('style')).toBe('transition: none; margin-top: -26px;');
-
- jest.runAllTimers();
-
- expect(nodes.wrapper.getAttribute('style')).toBe('max-height: 88px;');
- expect(nodes.table.getAttribute('style')).toBe('margin-top: 0px;');
- });
-});
-
function shallowRender(props: Partial<ComponentSourceSnippetGroupViewer['props']> = {}) {
const snippetGroup: SnippetGroup = {
component: mockSourceViewerFile(),
@@ -437,53 +305,8 @@ function shallowRender(props: Partial<ComponentSourceSnippetGroupViewer['props']
onIssuePopupToggle={jest.fn()}
onLocationSelect={jest.fn()}
renderDuplicationPopup={jest.fn()}
- scroll={jest.fn()}
snippetGroup={snippetGroup}
{...props}
/>
);
}
-
-function mockDom(refNode: HTMLDivElement) {
- refNode.querySelector = jest.fn(query => {
- const index = query.split('-').pop();
-
- switch (index) {
- case '0':
- return null;
- case '1':
- return mount(<div />).getDOMNode();
- case '2':
- return mount(
- <div>
- <div className="snippet" />
- </div>
- ).getDOMNode();
- case '3':
- return mount(
- <div>
- <div className="snippet">
- <div />
- </div>
- </div>
- ).getDOMNode();
- default:
- return null;
- }
- });
-}
-
-function mockDomForSizes(
- componentWrapper: ReactWrapper<{}, {}, ComponentSourceSnippetGroupViewer>,
- { wrapperHeight = 0, tableHeight = 0 }
-) {
- const wrapper = mount(<div className="snippet" />).getDOMNode();
- wrapper.getBoundingClientRect = jest.fn().mockReturnValue({ height: wrapperHeight });
- const table = mount(<div />).getDOMNode();
- table.getBoundingClientRect = jest.fn().mockReturnValue({ height: tableHeight });
- componentWrapper.instance().getNodes = jest.fn().mockReturnValue({
- wrapper,
- table
- });
- return { wrapper, table };
-}
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/CrossComponentSourceViewer-test.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/CrossComponentSourceViewer-test.tsx
index 9f6472f08e3..1425e69291c 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/CrossComponentSourceViewer-test.tsx
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/CrossComponentSourceViewer-test.tsx
@@ -28,6 +28,7 @@ import {
} from '../../../../helpers/mocks/sources';
import { mockFlowLocation, mockIssue } from '../../../../helpers/testMocks';
import { waitAndUpdate } from '../../../../helpers/testUtils';
+import ComponentSourceSnippetGroupViewer from '../ComponentSourceSnippetGroupViewer';
import CrossComponentSourceViewer from '../CrossComponentSourceViewer';
jest.mock('../../../../api/issues', () => {
@@ -102,10 +103,10 @@ it('should handle duplication popup', async () => {
const wrapper = shallowRender();
await waitAndUpdate(wrapper);
- wrapper.find('ComponentSourceSnippetGroupViewer').prop<Function>('loadDuplications')(
- 'foo',
- mockSourceLine()
- );
+ wrapper
+ .find(ComponentSourceSnippetGroupViewer)
+ .props()
+ .loadDuplications('foo', mockSourceLine());
await waitAndUpdate(wrapper);
expect(getDuplications).toHaveBeenCalledWith({ key: 'foo' });
@@ -114,11 +115,10 @@ it('should handle duplication popup', async () => {
expect(wrapper.state('duplicationsByLine')).toEqual({ '1': [0], '2': [0] });
expect(
- wrapper.find('ComponentSourceSnippetGroupViewer').prop<Function>('renderDuplicationPopup')(
- mockSourceViewerFile(),
- 0,
- 16
- )
+ wrapper
+ .find(ComponentSourceSnippetGroupViewer)
+ .props()
+ .renderDuplicationPopup(mockSourceViewerFile(), 0, 16)
).toMatchSnapshot();
});
@@ -127,14 +127,17 @@ function shallowRender(props: Partial<CrossComponentSourceViewer['props']> = {})
<CrossComponentSourceViewer
branchLike={undefined}
highlightedLocationMessage={undefined}
- issue={mockIssue(true, { key: '1' })}
+ issue={mockIssue(true, {
+ key: '1',
+ component: 'project:main.js',
+ textRange: { startLine: 1, endLine: 2, startOffset: 0, endOffset: 15 }
+ })}
issues={[]}
- locations={[mockFlowLocation()]}
+ locations={[mockFlowLocation({ component: 'project:main.js' })]}
onIssueChange={jest.fn()}
onLoaded={jest.fn()}
onIssueSelect={jest.fn()}
onLocationSelect={jest.fn()}
- scroll={jest.fn()}
selectedFlowIndex={0}
{...props}
/>
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 614c63fc735..1a3c685d6f8 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
@@ -17,11 +17,10 @@
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { mount, shallow } from 'enzyme';
+import { shallow } from 'enzyme';
import { range } from 'lodash';
import * as React from 'react';
import { mockSourceLine, mockSourceViewerFile } from '../../../../helpers/mocks/sources';
-import { scrollHorizontally } from '../../../../helpers/scrolling';
import { mockIssue } from '../../../../helpers/testMocks';
import SnippetViewer from '../SnippetViewer';
@@ -115,29 +114,6 @@ it('should correctly handle expansion', () => {
expect(expandBlock).toHaveBeenCalledWith(2, 'down');
});
-it('should handle scrolling', () => {
- const scroll = jest.fn();
- const wrapper = mountRender({ scroll });
-
- const element = {} as HTMLElement;
-
- wrapper.instance().doScroll(element);
-
- expect(scroll).toHaveBeenCalledWith(element);
-
- expect(scrollHorizontally).toHaveBeenCalled();
- expect((scrollHorizontally as jest.Mock).mock.calls[0][0]).toBe(element);
-});
-
-it('should handle scrolling to expanded row', () => {
- const scroll = jest.fn();
- const wrapper = mountRender({ scroll });
-
- wrapper.instance().scrollToLastExpandedRow();
-
- expect(scroll).toHaveBeenCalled();
-});
-
function shallowRender(props: Partial<SnippetViewer['props']> = {}) {
return shallow<SnippetViewer>(
<SnippetViewer
@@ -156,34 +132,8 @@ function shallowRender(props: Partial<SnippetViewer['props']> = {}) {
locationsByLine={{}}
onLocationSelect={jest.fn()}
renderDuplicationPopup={jest.fn()}
- scroll={jest.fn()}
snippet={[]}
{...props}
/>
);
}
-
-function mountRender(props: Partial<SnippetViewer['props']> = {}) {
- return mount<SnippetViewer>(
- <SnippetViewer
- component={mockSourceViewerFile()}
- duplications={undefined}
- duplicationsByLine={undefined}
- expandBlock={jest.fn()}
- handleSymbolClick={jest.fn()}
- highlightedLocationMessage={{ index: 0, text: '' }}
- highlightedSymbols={[]}
- index={0}
- issue={mockIssue()}
- lastSnippetOfLastGroup={false}
- loadDuplications={jest.fn()}
- locations={[]}
- locationsByLine={{}}
- onLocationSelect={jest.fn()}
- renderDuplicationPopup={jest.fn()}
- scroll={jest.fn()}
- snippet={[mockSourceLine()]}
- {...props}
- />
- );
-}
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 9825cb22d5c..98d86611f24 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
@@ -1,9 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`should render correctly 1`] = `
-<div
- className="component-source-container"
->
+<Fragment>
<IssueSourceViewerHeader
branchLike={
Object {
@@ -37,5 +35,5 @@ exports[`should render correctly 1`] = `
}
}
/>
-</div>
+</Fragment>
`;
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/CrossComponentSourceViewer-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/CrossComponentSourceViewer-test.tsx.snap
index f587717fbbc..489fd62f4f0 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/CrossComponentSourceViewer-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/CrossComponentSourceViewer-test.tsx.snap
@@ -22,30 +22,13 @@ exports[`should render correctly 1`] = `
`;
exports[`should render correctly 2`] = `
-<div>
+<Fragment>
<ContextProvider
key="1-0-0"
value={
Object {
"branchLike": undefined,
- "file": Object {
- "canMarkAsFavorite": true,
- "fav": false,
- "key": "project:main.js",
- "longName": "main.js",
- "measures": Object {
- "coverage": "85.2",
- "duplicationDensity": "1.0",
- "issues": "12",
- "lines": "56",
- },
- "name": "main.js",
- "path": "main.js",
- "project": "project",
- "projectName": "MyProject",
- "q": "FIL",
- "uuid": "foo-bar",
- },
+ "file": Object {},
}
}
>
@@ -55,7 +38,7 @@ exports[`should render correctly 2`] = `
issue={
Object {
"actions": Array [],
- "component": "main.js",
+ "component": "project:main.js",
"componentLongName": "main.js",
"componentQualifier": "FIL",
"componentUuid": "foo1234",
@@ -143,9 +126,9 @@ exports[`should render correctly 2`] = `
"severity": "MAJOR",
"status": "OPEN",
"textRange": Object {
- "endLine": 26,
+ "endLine": 2,
"endOffset": 15,
- "startLine": 25,
+ "startLine": 1,
"startOffset": 0,
},
"transitions": Array [],
@@ -158,7 +141,7 @@ exports[`should render correctly 2`] = `
locations={
Array [
Object {
- "component": "main.js",
+ "component": "project:main.js",
"index": 0,
"textRange": Object {
"endLine": 2,
@@ -174,30 +157,12 @@ exports[`should render correctly 2`] = `
onIssueSelect={[MockFunction]}
onLocationSelect={[MockFunction]}
renderDuplicationPopup={[Function]}
- scroll={[MockFunction]}
snippetGroup={
Object {
- "component": Object {
- "canMarkAsFavorite": true,
- "fav": false,
- "key": "project:main.js",
- "longName": "main.js",
- "measures": Object {
- "coverage": "85.2",
- "duplicationDensity": "1.0",
- "issues": "12",
- "lines": "56",
- },
- "name": "main.js",
- "path": "main.js",
- "project": "project",
- "projectName": "MyProject",
- "q": "FIL",
- "uuid": "foo-bar",
- },
+ "component": Object {},
"locations": Array [
Object {
- "component": "main.js",
+ "component": "project:main.js",
"index": 0,
"textRange": Object {
"endLine": 2,
@@ -207,28 +172,16 @@ exports[`should render correctly 2`] = `
},
},
],
- "sources": Object {
- "16": 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",
- },
- },
+ "sources": Array [],
}
}
/>
</ContextProvider>
-</div>
+</Fragment>
`;
exports[`should render correctly: no component found 1`] = `
-<div>
+<Fragment>
<ContextProvider
key="unknown-0-0"
value={
@@ -350,7 +303,6 @@ exports[`should render correctly: no component found 1`] = `
onIssueSelect={[MockFunction]}
onLocationSelect={[MockFunction]}
renderDuplicationPopup={[Function]}
- scroll={[MockFunction]}
snippetGroup={
Object {
"component": Object {},
@@ -365,24 +317,7 @@ exports[`should render correctly: no component found 1`] = `
value={
Object {
"branchLike": undefined,
- "file": Object {
- "canMarkAsFavorite": true,
- "fav": false,
- "key": "project:main.js",
- "longName": "main.js",
- "measures": Object {
- "coverage": "85.2",
- "duplicationDensity": "1.0",
- "issues": "12",
- "lines": "56",
- },
- "name": "main.js",
- "path": "main.js",
- "project": "project",
- "projectName": "MyProject",
- "q": "FIL",
- "uuid": "foo-bar",
- },
+ "file": Object {},
}
}
>
@@ -495,7 +430,7 @@ exports[`should render correctly: no component found 1`] = `
locations={
Array [
Object {
- "component": "main.js",
+ "component": "project:main.js",
"index": 0,
"textRange": Object {
"endLine": 2,
@@ -511,30 +446,12 @@ exports[`should render correctly: no component found 1`] = `
onIssueSelect={[MockFunction]}
onLocationSelect={[MockFunction]}
renderDuplicationPopup={[Function]}
- scroll={[MockFunction]}
snippetGroup={
Object {
- "component": Object {
- "canMarkAsFavorite": true,
- "fav": false,
- "key": "project:main.js",
- "longName": "main.js",
- "measures": Object {
- "coverage": "85.2",
- "duplicationDensity": "1.0",
- "issues": "12",
- "lines": "56",
- },
- "name": "main.js",
- "path": "main.js",
- "project": "project",
- "projectName": "MyProject",
- "q": "FIL",
- "uuid": "foo-bar",
- },
+ "component": Object {},
"locations": Array [
Object {
- "component": "main.js",
+ "component": "project:main.js",
"index": 0,
"textRange": Object {
"endLine": 2,
@@ -544,22 +461,10 @@ exports[`should render correctly: no component found 1`] = `
},
},
],
- "sources": Object {
- "16": 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",
- },
- },
+ "sources": Array [],
}
}
/>
</ContextProvider>
-</div>
+</Fragment>
`;
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/IssueSourceViewerHeader-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/IssueSourceViewerHeader-test.tsx.snap
index 0b6e42a0fb3..fb3dd212fc1 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/IssueSourceViewerHeader-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/IssueSourceViewerHeader-test.tsx.snap
@@ -2,7 +2,9 @@
exports[`should render correctly 1`] = `
<div
- className="source-viewer-header-slim display-flex-row display-flex-space-between"
+ aria-label="foo/bar.ts"
+ className="issue-source-viewer-header display-flex-row display-flex-space-between"
+ role="separator"
>
<div
className="display-flex-center flex-1"
@@ -82,7 +84,9 @@ exports[`should render correctly 1`] = `
exports[`should render correctly: no link to project 1`] = `
<div
- className="source-viewer-header-slim display-flex-row display-flex-space-between"
+ aria-label="foo/bar.ts"
+ className="issue-source-viewer-header display-flex-row display-flex-space-between"
+ role="separator"
>
<div
className="display-flex-center flex-1"
@@ -157,7 +161,9 @@ exports[`should render correctly: no link to project 1`] = `
exports[`should render correctly: no project name 1`] = `
<div
- className="source-viewer-header-slim display-flex-row display-flex-space-between"
+ aria-label="foo/bar.ts"
+ className="issue-source-viewer-header display-flex-row display-flex-space-between"
+ role="separator"
>
<div
className="display-flex-center flex-1"
@@ -221,7 +227,9 @@ exports[`should render correctly: no project name 1`] = `
exports[`should render correctly: project root 1`] = `
<div
- className="source-viewer-header-slim display-flex-row display-flex-space-between"
+ aria-label="foo/bar.ts"
+ className="issue-source-viewer-header display-flex-row display-flex-space-between"
+ role="separator"
>
<div
className="display-flex-center flex-1"
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 085138cd9cf..0ee54422c94 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
@@ -55,7 +55,6 @@ exports[`should render correctly 1`] = `
onSymbolClick={[MockFunction]}
openIssues={false}
renderDuplicationPopup={[MockFunction]}
- scroll={[Function]}
secondaryIssueLocations={Array []}
verticalBuffer={0}
/>
@@ -107,7 +106,6 @@ exports[`should render correctly 1`] = `
}
}
renderDuplicationPopup={[MockFunction]}
- scroll={[Function]}
secondaryIssueLocations={Array []}
verticalBuffer={0}
/>
@@ -159,7 +157,6 @@ exports[`should render correctly 1`] = `
}
}
renderDuplicationPopup={[MockFunction]}
- scroll={[Function]}
secondaryIssueLocations={Array []}
verticalBuffer={0}
/>
@@ -235,7 +232,6 @@ exports[`should render correctly when at the bottom of the file 1`] = `
onSymbolClick={[MockFunction]}
openIssues={false}
renderDuplicationPopup={[MockFunction]}
- scroll={[Function]}
secondaryIssueLocations={Array []}
verticalBuffer={0}
/>
@@ -287,7 +283,6 @@ exports[`should render correctly when at the bottom of the file 1`] = `
}
}
renderDuplicationPopup={[MockFunction]}
- scroll={[Function]}
secondaryIssueLocations={Array []}
verticalBuffer={0}
/>
@@ -339,7 +334,6 @@ exports[`should render correctly when at the bottom of the file 1`] = `
}
}
renderDuplicationPopup={[MockFunction]}
- scroll={[Function]}
secondaryIssueLocations={Array []}
verticalBuffer={0}
/>
@@ -391,7 +385,6 @@ exports[`should render correctly when at the bottom of the file 1`] = `
}
}
renderDuplicationPopup={[MockFunction]}
- scroll={[Function]}
secondaryIssueLocations={Array []}
verticalBuffer={0}
/>
@@ -456,7 +449,6 @@ exports[`should render correctly when at the top of the file 1`] = `
onSymbolClick={[MockFunction]}
openIssues={false}
renderDuplicationPopup={[MockFunction]}
- scroll={[Function]}
secondaryIssueLocations={Array []}
verticalBuffer={0}
/>
@@ -508,7 +500,6 @@ exports[`should render correctly when at the top of the file 1`] = `
}
}
renderDuplicationPopup={[MockFunction]}
- scroll={[Function]}
secondaryIssueLocations={Array []}
verticalBuffer={0}
/>
@@ -560,7 +551,6 @@ exports[`should render correctly when at the top of the file 1`] = `
}
}
renderDuplicationPopup={[MockFunction]}
- scroll={[Function]}
secondaryIssueLocations={Array []}
verticalBuffer={0}
/>
@@ -612,7 +602,6 @@ exports[`should render correctly when at the top of the file 1`] = `
}
}
renderDuplicationPopup={[MockFunction]}
- scroll={[Function]}
secondaryIssueLocations={Array []}
verticalBuffer={0}
/>
@@ -664,7 +653,6 @@ exports[`should render correctly when at the top of the file 1`] = `
}
}
renderDuplicationPopup={[MockFunction]}
- scroll={[Function]}
secondaryIssueLocations={Array []}
verticalBuffer={0}
/>
@@ -716,7 +704,6 @@ exports[`should render correctly when at the top of the file 1`] = `
}
}
renderDuplicationPopup={[MockFunction]}
- scroll={[Function]}
secondaryIssueLocations={Array []}
verticalBuffer={0}
/>
@@ -768,7 +755,6 @@ exports[`should render correctly when at the top of the file 1`] = `
}
}
renderDuplicationPopup={[MockFunction]}
- scroll={[Function]}
secondaryIssueLocations={Array []}
verticalBuffer={0}
/>
@@ -845,7 +831,6 @@ exports[`should render correctly with no SCM 1`] = `
onSymbolClick={[MockFunction]}
openIssues={false}
renderDuplicationPopup={[MockFunction]}
- scroll={[Function]}
secondaryIssueLocations={Array []}
verticalBuffer={0}
/>
@@ -898,7 +883,6 @@ exports[`should render correctly with no SCM 1`] = `
}
}
renderDuplicationPopup={[MockFunction]}
- scroll={[Function]}
secondaryIssueLocations={Array []}
verticalBuffer={0}
/>
@@ -951,7 +935,6 @@ exports[`should render correctly with no SCM 1`] = `
}
}
renderDuplicationPopup={[MockFunction]}
- scroll={[Function]}
secondaryIssueLocations={Array []}
verticalBuffer={0}
/>
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/utils-test.ts b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/utils-test.ts
index dadf1c31958..bc9896d68a6 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/utils-test.ts
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/utils-test.ts
@@ -23,7 +23,7 @@ import { createSnippets, expandSnippet, groupLocationsByComponent } from '../uti
describe('groupLocationsByComponent', () => {
it('should handle empty args', () => {
- expect(groupLocationsByComponent(mockIssue(), [], {})).toEqual([]);
+ expect(groupLocationsByComponent(mockIssue(), [], {})).toEqual([{ locations: [] }]);
});
it('should group correctly', () => {
diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/utils.ts b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/utils.ts
index d41b61cbe0d..7476c471fb1 100644
--- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/utils.ts
+++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/utils.ts
@@ -181,6 +181,10 @@ export function groupLocationsByComponent(
currentGroup.locations.push(loc);
});
+ if (groups.length === 0) {
+ groups.push({ locations: [], ...components[issue.component] });
+ }
+
return groups;
}