3 * Copyright (C) 2009-2019 SonarSource SA
4 * mailto:info AT sonarsource DOT com
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.
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.
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.
20 import { mount, ReactWrapper, shallow } from 'enzyme';
21 import { range, times } from 'lodash';
22 import * as React from 'react';
23 import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
24 import { getSources } from '../../../../api/components';
29 mockShortLivingBranch,
30 mockSnippetsByComponent,
33 } from '../../../../helpers/testMocks';
34 import ComponentSourceSnippetViewer from '../ComponentSourceSnippetViewer';
36 jest.mock('../../../../api/components', () => ({
37 getSources: jest.fn().mockResolvedValue([])
44 it('should render correctly', () => {
45 expect(shallowRender()).toMatchSnapshot();
48 it('should render correctly with secondary locations', () => {
49 // issue with secondary locations but no flows
50 const issue = mockIssue(true, {
52 textRange: { startLine: 5, endLine: 5, startOffset: 5, endOffset: 10 }
55 const snippetGroup: T.SnippetGroup = {
59 textRange: { startLine: 34, endLine: 34, startOffset: 0, endOffset: 0 }
63 textRange: { startLine: 54, endLine: 54, startOffset: 0, endOffset: 0 }
66 ...mockSnippetsByComponent('a', [...range(3, 15), 32, 33, 34, 35, 36, 52, 53, 54, 55, 56])
68 const wrapper = shallowRender({ issue, snippetGroup });
69 expect(wrapper.state('snippets')).toHaveLength(3);
70 expect(wrapper.state('snippets')[0]).toEqual({ index: 0, start: 3, end: 14 });
71 expect(wrapper.state('snippets')[1]).toEqual({ index: 1, start: 32, end: 36 });
72 expect(wrapper.state('snippets')[2]).toEqual({ index: 2, start: 52, end: 56 });
75 it('should expand block', async () => {
76 (getSources as jest.Mock).mockResolvedValueOnce(
77 Object.values(mockSnippetsByComponent('a', [22, 23, 24, 25, 26, 27, 28, 29, 30, 31]).sources)
79 const snippetGroup: T.SnippetGroup = {
83 textRange: { startLine: 34, endLine: 34, startOffset: 0, endOffset: 0 }
87 textRange: { startLine: 54, endLine: 54, startOffset: 0, endOffset: 0 }
90 ...mockSnippetsByComponent('a', [32, 33, 34, 35, 36, 52, 53, 54, 55, 56])
93 const wrapper = shallowRender({ snippetGroup });
95 wrapper.instance().expandBlock(0, 'up');
96 await waitAndUpdate(wrapper);
98 expect(getSources).toHaveBeenCalledWith({ from: 19, key: 'a', to: 31 });
99 expect(wrapper.state('snippets')).toHaveLength(2);
100 expect(wrapper.state('snippets')[0]).toEqual({ index: 0, start: 22, end: 36 });
101 expect(Object.keys(wrapper.state('additionalLines'))).toHaveLength(10);
104 it('should expand full component', async () => {
105 (getSources as jest.Mock).mockResolvedValueOnce(
106 Object.values(mockSnippetsByComponent('a', times(14)).sources)
108 const snippetGroup: T.SnippetGroup = {
112 textRange: { startLine: 3, endLine: 3, startOffset: 0, endOffset: 0 }
116 textRange: { startLine: 12, endLine: 12, startOffset: 0, endOffset: 0 }
119 ...mockSnippetsByComponent('a', [1, 2, 3, 4, 5, 10, 11, 12, 13, 14])
122 const wrapper = shallowRender({ snippetGroup });
124 wrapper.instance().expandComponent();
125 await waitAndUpdate(wrapper);
127 expect(getSources).toHaveBeenCalledWith({ key: 'a' });
128 expect(wrapper.state('snippets')).toHaveLength(1);
129 expect(wrapper.state('snippets')[0]).toEqual({ index: -1, start: 0, end: 13 });
132 it('should get the right branch when expanding', async () => {
133 (getSources as jest.Mock).mockResolvedValueOnce(
135 mockSnippetsByComponent('a', [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]).sources
138 const snippetGroup: T.SnippetGroup = {
139 locations: [mockFlowLocation()],
140 ...mockSnippetsByComponent('a', [1, 2, 3, 4])
143 const wrapper = shallowRender({
144 branchLike: mockShortLivingBranch({ name: 'asdf' }),
148 wrapper.instance().expandBlock(0, 'down');
149 await waitAndUpdate(wrapper);
151 expect(getSources).toHaveBeenCalledWith({ branch: 'asdf', from: 5, key: 'a', to: 17 });
154 it('should handle correctly open/close issue', () => {
155 const wrapper = shallowRender();
156 const sourceLine = mockSourceLine();
157 expect(wrapper.state('openIssuesByLine')).toEqual({});
158 wrapper.instance().handleOpenIssues(sourceLine);
159 expect(wrapper.state('openIssuesByLine')).toEqual({ [sourceLine.line]: true });
160 wrapper.instance().handleCloseIssues(sourceLine);
161 expect(wrapper.state('openIssuesByLine')).toEqual({ [sourceLine.line]: false });
164 it('should handle symbol highlighting', () => {
165 const wrapper = shallowRender();
166 expect(wrapper.state('highlightedSymbols')).toEqual([]);
167 wrapper.instance().handleSymbolClick(['foo']);
168 expect(wrapper.state('highlightedSymbols')).toEqual(['foo']);
171 it('should correctly handle lines actions', () => {
172 const snippetGroup: T.SnippetGroup = {
176 textRange: { startLine: 34, endLine: 34, startOffset: 0, endOffset: 0 }
180 textRange: { startLine: 54, endLine: 54, startOffset: 0, endOffset: 0 }
183 ...mockSnippetsByComponent('a', [32, 33, 34, 35, 36, 52, 53, 54, 55, 56])
185 const loadDuplications = jest.fn();
186 const onLinePopupToggle = jest.fn();
187 const renderDuplicationPopup = jest.fn();
189 const wrapper = shallowRender({
192 renderDuplicationPopup,
196 const line = mockSourceLine();
198 .find('SnippetViewer')
200 .prop<Function>('loadDuplications')(line);
201 expect(loadDuplications).toHaveBeenCalledWith('a', line);
204 .find('SnippetViewer')
206 .prop<Function>('handleLinePopupToggle')({ line: 13, name: 'foo' });
207 expect(onLinePopupToggle).toHaveBeenCalledWith({ component: 'a', line: 13, name: 'foo' });
210 .find('SnippetViewer')
212 .prop<Function>('renderDuplicationPopup')(1, 13);
213 expect(renderDuplicationPopup).toHaveBeenCalledWith(
214 mockSourceViewerFile({ key: 'a', path: 'a' }),
220 describe('getNodes', () => {
221 const snippetGroup: T.SnippetGroup = {
222 component: mockSourceViewerFile(),
226 const wrapper = mount<ComponentSourceSnippetViewer>(
227 <ComponentSourceSnippetViewer
228 branchLike={mockMainBranch()}
229 duplications={undefined}
230 duplicationsByLine={undefined}
231 highlightedLocationMessage={{ index: 0, text: '' }}
235 linePopup={undefined}
236 loadDuplications={jest.fn()}
238 onIssueChange={jest.fn()}
239 onIssuePopupToggle={jest.fn()}
240 onLinePopupToggle={jest.fn()}
241 onLocationSelect={jest.fn()}
242 renderDuplicationPopup={jest.fn()}
244 snippetGroup={snippetGroup}
248 it('should return undefined if any node is missing', async () => {
249 await waitAndUpdate(wrapper);
250 const rootNode = wrapper.instance().rootNodeRef;
251 mockDom(rootNode.current!);
252 expect(wrapper.instance().getNodes(0)).toBeUndefined();
253 expect(wrapper.instance().getNodes(1)).toBeUndefined();
254 expect(wrapper.instance().getNodes(2)).toBeUndefined();
257 it('should return elements if dom is correct', async () => {
258 await waitAndUpdate(wrapper);
259 const rootNode = wrapper.instance().rootNodeRef;
260 mockDom(rootNode.current!);
261 expect(wrapper.instance().getNodes(3)).not.toBeUndefined();
265 describe('getHeight', () => {
266 jest.useFakeTimers();
268 const snippetGroup: T.SnippetGroup = {
269 component: mockSourceViewerFile(),
273 const wrapper = mount<ComponentSourceSnippetViewer>(
274 <ComponentSourceSnippetViewer
275 branchLike={mockMainBranch()}
276 duplications={undefined}
277 duplicationsByLine={undefined}
278 highlightedLocationMessage={{ index: 0, text: '' }}
282 linePopup={undefined}
283 loadDuplications={jest.fn()}
285 onIssueChange={jest.fn()}
286 onIssuePopupToggle={jest.fn()}
287 onLinePopupToggle={jest.fn()}
288 onLocationSelect={jest.fn()}
289 renderDuplicationPopup={jest.fn()}
291 snippetGroup={snippetGroup}
295 it('should set maxHeight to current height', async () => {
296 await waitAndUpdate(wrapper);
298 const nodes = mockDomForSizes(wrapper, { wrapperHeight: 42, tableHeight: 68 });
299 wrapper.instance().setMaxHeight(0);
301 expect(nodes.wrapper.getAttribute('style')).toBe('max-height: 88px;');
302 expect(nodes.table.getAttribute('style')).toBeNull();
305 it('should set margin and then maxHeight for a nice upwards animation', async () => {
306 await waitAndUpdate(wrapper);
308 const nodes = mockDomForSizes(wrapper, { wrapperHeight: 42, tableHeight: 68 });
309 wrapper.instance().setMaxHeight(0, undefined, true);
311 expect(nodes.wrapper.getAttribute('style')).toBeNull();
312 expect(nodes.table.getAttribute('style')).toBe('transition: none; margin-top: -26px;');
316 expect(nodes.wrapper.getAttribute('style')).toBe('max-height: 88px;');
317 expect(nodes.table.getAttribute('style')).toBe('margin-top: 0px;');
321 function shallowRender(props: Partial<ComponentSourceSnippetViewer['props']> = {}) {
322 const snippetGroup: T.SnippetGroup = {
323 component: mockSourceViewerFile(),
327 return shallow<ComponentSourceSnippetViewer>(
328 <ComponentSourceSnippetViewer
329 branchLike={mockMainBranch()}
330 duplications={undefined}
331 duplicationsByLine={undefined}
332 highlightedLocationMessage={{ index: 0, text: '' }}
336 linePopup={undefined}
337 loadDuplications={jest.fn()}
339 onIssueChange={jest.fn()}
340 onIssuePopupToggle={jest.fn()}
341 onLinePopupToggle={jest.fn()}
342 onLocationSelect={jest.fn()}
343 renderDuplicationPopup={jest.fn()}
345 snippetGroup={snippetGroup}
351 function mockDom(refNode: HTMLDivElement) {
352 refNode.querySelector = jest.fn(query => {
353 const index = query.split('-').pop();
359 return mount(<div />).getDOMNode();
363 <div className="snippet" />
369 <div className="snippet">
380 function mockDomForSizes(
381 componentWrapper: ReactWrapper<{}, {}, ComponentSourceSnippetViewer>,
382 { wrapperHeight = 0, tableHeight = 0 }
384 const wrapper = mount(<div className="snippet" />).getDOMNode();
385 wrapper.getBoundingClientRect = jest.fn().mockReturnValue({ height: wrapperHeight });
386 const table = mount(<div />).getDOMNode();
387 table.getBoundingClientRect = jest.fn().mockReturnValue({ height: tableHeight });
388 componentWrapper.instance().getNodes = jest.fn().mockReturnValue({
392 return { wrapper, table };