]> source.dussan.org Git - sonarqube.git/blob
30ca9f59af4151052917dbe7c4c847bbbf1de121
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2022 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 import { shallow } from 'enzyme';
21 import { range, times } from 'lodash';
22 import * as React from 'react';
23 import { getSources } from '../../../../api/components';
24 import IssueMessageBox from '../../../../components/issue/IssueMessageBox';
25 import { mockBranch, mockMainBranch } from '../../../../helpers/mocks/branch-like';
26 import {
27   mockSnippetsByComponent,
28   mockSourceLine,
29   mockSourceViewerFile
30 } from '../../../../helpers/mocks/sources';
31 import { mockFlowLocation, mockIssue } from '../../../../helpers/testMocks';
32 import { waitAndUpdate } from '../../../../helpers/testUtils';
33 import { SnippetGroup } from '../../../../types/types';
34 import ComponentSourceSnippetGroupViewer from '../ComponentSourceSnippetGroupViewer';
35 import SnippetViewer from '../SnippetViewer';
36
37 jest.mock('../../../../api/components', () => ({
38   getSources: jest.fn().mockResolvedValue([])
39 }));
40
41 beforeEach(() => {
42   jest.clearAllMocks();
43 });
44
45 it('should render correctly', () => {
46   expect(shallowRender()).toMatchSnapshot();
47 });
48
49 it('should render correctly with secondary locations', () => {
50   // issue with secondary locations but no flows
51   const issue = mockIssue(true, {
52     component: 'project:main.js',
53     flows: [],
54     textRange: { startLine: 7, endLine: 7, startOffset: 5, endOffset: 10 }
55   });
56
57   const snippetGroup: SnippetGroup = {
58     locations: [
59       mockFlowLocation({
60         component: issue.component,
61         textRange: { startLine: 34, endLine: 34, startOffset: 0, endOffset: 0 }
62       }),
63       mockFlowLocation({
64         component: issue.component,
65         textRange: { startLine: 74, endLine: 74, startOffset: 0, endOffset: 0 }
66       })
67     ],
68     ...mockSnippetsByComponent('main.js', 'project', [
69       ...range(2, 17),
70       ...range(29, 39),
71       ...range(69, 79)
72     ])
73   };
74   const wrapper = shallowRender({ issue, snippetGroup });
75   expect(wrapper.state('snippets')).toHaveLength(3);
76   expect(wrapper.state('snippets')[0]).toEqual({ index: 0, start: 2, end: 16 });
77   expect(wrapper.state('snippets')[1]).toEqual({ index: 1, start: 29, end: 39 });
78   expect(wrapper.state('snippets')[2]).toEqual({ index: 2, start: 69, end: 79 });
79 });
80
81 it('should render correctly with flows', () => {
82   // issue with flows but no secondary locations
83   const issue = mockIssue(true, {
84     component: 'project:main.js',
85     secondaryLocations: [],
86     textRange: { startLine: 7, endLine: 7, startOffset: 5, endOffset: 10 }
87   });
88
89   const snippetGroup: SnippetGroup = {
90     locations: [
91       mockFlowLocation({
92         component: issue.component,
93         textRange: { startLine: 34, endLine: 34, startOffset: 0, endOffset: 0 }
94       }),
95       mockFlowLocation({
96         component: issue.component,
97         textRange: { startLine: 74, endLine: 74, startOffset: 0, endOffset: 0 }
98       })
99     ],
100     ...mockSnippetsByComponent('main.js', 'project', [
101       ...range(2, 17),
102       ...range(29, 39),
103       ...range(69, 79)
104     ])
105   };
106   const wrapper = shallowRender({ issue, snippetGroup });
107   expect(wrapper.state('snippets')).toHaveLength(2);
108   expect(wrapper.state('snippets')[0]).toEqual({ index: 0, start: 29, end: 39 });
109   expect(wrapper.state('snippets')[1]).toEqual({ index: 1, start: 69, end: 79 });
110
111   // Check that locationsByLine is defined when isLastOccurenceOfPrimaryComponent
112   expect(
113     wrapper
114       .find(SnippetViewer)
115       .at(0)
116       .props().locationsByLine
117   ).not.toEqual({});
118
119   // If not, it should be an empty object:
120   const snippets = shallowRender({
121     isLastOccurenceOfPrimaryComponent: false,
122     issue,
123     snippetGroup
124   }).find(SnippetViewer);
125
126   expect(snippets.at(0).props().locationsByLine).toEqual({});
127   expect(snippets.at(1).props().locationsByLine).toEqual({});
128 });
129
130 it('should render file-level issue correctly', () => {
131   // issue with secondary locations and no primary location
132   const issue = mockIssue(true, {
133     component: 'project:main.js',
134     flows: [],
135     textRange: undefined
136   });
137
138   const wrapper = shallowRender({
139     issue,
140     snippetGroup: {
141       locations: [
142         mockFlowLocation({
143           component: issue.component,
144           textRange: { startLine: 34, endLine: 34, startOffset: 0, endOffset: 0 }
145         })
146       ],
147       ...mockSnippetsByComponent('main.js', 'project', range(29, 39))
148     }
149   });
150
151   expect(wrapper.find(IssueMessageBox).exists()).toBe(true);
152 });
153
154 it('should expand block', async () => {
155   (getSources as jest.Mock).mockResolvedValueOnce(
156     Object.values(mockSnippetsByComponent('a', 'project', range(6, 59)).sources)
157   );
158   const issue = mockIssue(true, {
159     textRange: { startLine: 74, endLine: 74, startOffset: 5, endOffset: 10 }
160   });
161   const snippetGroup: SnippetGroup = {
162     locations: [
163       mockFlowLocation({
164         component: 'a',
165         textRange: { startLine: 74, endLine: 74, startOffset: 0, endOffset: 0 }
166       }),
167       mockFlowLocation({
168         component: 'a',
169         textRange: { startLine: 107, endLine: 107, startOffset: 0, endOffset: 0 }
170       })
171     ],
172     ...mockSnippetsByComponent('a', 'project', [...range(69, 83), ...range(102, 112)])
173   };
174
175   const wrapper = shallowRender({ issue, snippetGroup });
176
177   wrapper.instance().expandBlock(0, 'up');
178   await waitAndUpdate(wrapper);
179
180   expect(getSources).toHaveBeenCalledWith({ from: 9, key: 'project:a', to: 68 });
181   expect(wrapper.state('snippets')).toHaveLength(2);
182   expect(wrapper.state('snippets')[0]).toEqual({ index: 0, start: 19, end: 83 });
183   expect(Object.keys(wrapper.state('additionalLines'))).toHaveLength(53);
184 });
185
186 it('should expand full component', async () => {
187   (getSources as jest.Mock).mockResolvedValueOnce(
188     Object.values(mockSnippetsByComponent('a', 'project', times(14)).sources)
189   );
190   const snippetGroup: SnippetGroup = {
191     locations: [
192       mockFlowLocation({
193         component: 'a',
194         textRange: { startLine: 3, endLine: 3, startOffset: 0, endOffset: 0 }
195       }),
196       mockFlowLocation({
197         component: 'a',
198         textRange: { startLine: 12, endLine: 12, startOffset: 0, endOffset: 0 }
199       })
200     ],
201     ...mockSnippetsByComponent('a', 'project', [1, 2, 3, 4, 5, 10, 11, 12, 13, 14])
202   };
203
204   const wrapper = shallowRender({ snippetGroup });
205
206   wrapper.instance().expandComponent();
207   await waitAndUpdate(wrapper);
208
209   expect(getSources).toHaveBeenCalledWith({ key: 'project:a' });
210   expect(wrapper.state('snippets')).toHaveLength(1);
211   expect(wrapper.state('snippets')[0]).toEqual({ index: -1, start: 0, end: 13 });
212 });
213
214 it('should get the right branch when expanding', async () => {
215   (getSources as jest.Mock).mockResolvedValueOnce(
216     Object.values(
217       mockSnippetsByComponent('a', 'project', [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17])
218         .sources
219     )
220   );
221   const snippetGroup: SnippetGroup = {
222     locations: [mockFlowLocation()],
223     ...mockSnippetsByComponent('a', 'project', [1, 2, 3, 4, 5, 6, 7])
224   };
225
226   const wrapper = shallowRender({
227     branchLike: mockBranch({ name: 'asdf' }),
228     snippetGroup
229   });
230
231   wrapper.instance().expandBlock(0, 'down');
232   await waitAndUpdate(wrapper);
233
234   expect(getSources).toHaveBeenCalledWith({ branch: 'asdf', from: 8, key: 'project:a', to: 67 });
235 });
236
237 it('should handle symbol highlighting', () => {
238   const wrapper = shallowRender();
239   expect(wrapper.state('highlightedSymbols')).toEqual([]);
240   wrapper.instance().handleSymbolClick(['foo']);
241   expect(wrapper.state('highlightedSymbols')).toEqual(['foo']);
242   wrapper.instance().handleSymbolClick(['foo']);
243   expect(wrapper.state('highlightedSymbols')).toEqual([]);
244 });
245
246 it('should correctly handle lines actions', () => {
247   const snippetGroup: SnippetGroup = {
248     locations: [
249       mockFlowLocation({
250         component: 'my-project:foo/bar.ts',
251         textRange: { startLine: 34, endLine: 34, startOffset: 0, endOffset: 0 }
252       }),
253       mockFlowLocation({
254         component: 'my-project:foo/bar.ts',
255         textRange: { startLine: 54, endLine: 54, startOffset: 0, endOffset: 0 }
256       })
257     ],
258     ...mockSnippetsByComponent('foo/bar.ts', 'my-project', [32, 33, 34, 35, 36, 52, 53, 54, 55, 56])
259   };
260   const loadDuplications = jest.fn();
261   const renderDuplicationPopup = jest.fn();
262
263   const wrapper = shallowRender({
264     loadDuplications,
265     renderDuplicationPopup,
266     snippetGroup
267   });
268
269   const line = mockSourceLine();
270   wrapper
271     .find('SnippetViewer')
272     .first()
273     .prop<Function>('loadDuplications')(line);
274   expect(loadDuplications).toHaveBeenCalledWith('my-project:foo/bar.ts', line);
275
276   wrapper
277     .find('SnippetViewer')
278     .first()
279     .prop<Function>('renderDuplicationPopup')(1, 13);
280   expect(renderDuplicationPopup).toHaveBeenCalledWith(
281     mockSourceViewerFile('foo/bar.ts', 'my-project'),
282     1,
283     13
284   );
285 });
286
287 function shallowRender(props: Partial<ComponentSourceSnippetGroupViewer['props']> = {}) {
288   const snippetGroup: SnippetGroup = {
289     component: mockSourceViewerFile(),
290     locations: [],
291     sources: []
292   };
293   return shallow<ComponentSourceSnippetGroupViewer>(
294     <ComponentSourceSnippetGroupViewer
295       branchLike={mockMainBranch()}
296       highlightedLocationMessage={{ index: 0, text: '' }}
297       isLastOccurenceOfPrimaryComponent={true}
298       issue={mockIssue()}
299       issuesByLine={{}}
300       lastSnippetGroup={false}
301       loadDuplications={jest.fn()}
302       locations={[]}
303       onIssueSelect={jest.fn()}
304       onLocationSelect={jest.fn()}
305       renderDuplicationPopup={jest.fn()}
306       snippetGroup={snippetGroup}
307       {...props}
308     />
309   );
310 }