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 * as React from 'react';
21 import { shallow, mount, ReactWrapper } from 'enzyme';
22 import { times } from 'lodash';
23 import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
24 import ComponentSourceSnippetViewer from '../ComponentSourceSnippetViewer';
30 mockSnippetsByComponent,
33 } from '../../../../helpers/testMocks';
34 import { getSources } from '../../../../api/components';
36 jest.mock('../../../../api/components', () => ({
37 getSources: jest.fn().mockResolvedValue([])
44 it('should render correctly', () => {
45 expect(shallowRender()).toMatchSnapshot();
48 it('should expand block', async () => {
49 (getSources as jest.Mock).mockResolvedValueOnce(
50 Object.values(mockSnippetsByComponent('a', [22, 23, 24, 25, 26, 27, 28, 29, 30, 31]).sources)
52 const snippetGroup: T.SnippetGroup = {
56 textRange: { startLine: 34, endLine: 34, startOffset: 0, endOffset: 0 }
60 textRange: { startLine: 54, endLine: 54, startOffset: 0, endOffset: 0 }
63 ...mockSnippetsByComponent('a', [32, 33, 34, 35, 36, 52, 53, 54, 55, 56])
66 const wrapper = shallowRender({ snippetGroup });
68 wrapper.instance().expandBlock(0, 'up');
69 await waitAndUpdate(wrapper);
71 expect(getSources).toHaveBeenCalledWith({ from: 19, key: 'a', to: 31 });
72 expect(wrapper.state('snippets')).toHaveLength(2);
73 expect(wrapper.state('snippets')[0]).toEqual({ index: 0, start: 22, end: 36 });
74 expect(Object.keys(wrapper.state('additionalLines'))).toHaveLength(10);
77 it('should expand full component', async () => {
78 (getSources as jest.Mock).mockResolvedValueOnce(
79 Object.values(mockSnippetsByComponent('a', times(14)).sources)
81 const snippetGroup: T.SnippetGroup = {
85 textRange: { startLine: 3, endLine: 3, startOffset: 0, endOffset: 0 }
89 textRange: { startLine: 12, endLine: 12, startOffset: 0, endOffset: 0 }
92 ...mockSnippetsByComponent('a', [1, 2, 3, 4, 5, 10, 11, 12, 13, 14])
95 const wrapper = shallowRender({ snippetGroup });
97 wrapper.instance().expandComponent();
98 await waitAndUpdate(wrapper);
100 expect(getSources).toHaveBeenCalledWith({ key: 'a' });
101 expect(wrapper.state('snippets')).toHaveLength(1);
102 expect(wrapper.state('snippets')[0]).toEqual({ index: -1, start: 0, end: 13 });
105 it('should get the right branch when expanding', async () => {
106 (getSources as jest.Mock).mockResolvedValueOnce(
108 mockSnippetsByComponent('a', [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]).sources
111 const snippetGroup: T.SnippetGroup = {
112 locations: [mockFlowLocation()],
113 ...mockSnippetsByComponent('a', [1, 2, 3, 4])
116 const wrapper = shallowRender({
117 branchLike: mockShortLivingBranch({ name: 'asdf' }),
121 wrapper.instance().expandBlock(0, 'down');
122 await waitAndUpdate(wrapper);
124 expect(getSources).toHaveBeenCalledWith({ branch: 'asdf', from: 5, key: 'a', to: 17 });
127 it('should handle correctly open/close issue', () => {
128 const wrapper = shallowRender();
129 const sourceLine = mockSourceLine();
130 expect(wrapper.state('openIssuesByLine')).toEqual({});
131 wrapper.instance().handleOpenIssues(sourceLine);
132 expect(wrapper.state('openIssuesByLine')).toEqual({ [sourceLine.line]: true });
133 wrapper.instance().handleCloseIssues(sourceLine);
134 expect(wrapper.state('openIssuesByLine')).toEqual({ [sourceLine.line]: false });
137 it('should handle symbol highlighting', () => {
138 const wrapper = shallowRender();
139 expect(wrapper.state('highlightedSymbols')).toEqual([]);
140 wrapper.instance().handleSymbolClick(['foo']);
141 expect(wrapper.state('highlightedSymbols')).toEqual(['foo']);
144 it('should correctly handle lines actions', () => {
145 const snippetGroup: T.SnippetGroup = {
149 textRange: { startLine: 34, endLine: 34, startOffset: 0, endOffset: 0 }
153 textRange: { startLine: 54, endLine: 54, startOffset: 0, endOffset: 0 }
156 ...mockSnippetsByComponent('a', [32, 33, 34, 35, 36, 52, 53, 54, 55, 56])
158 const loadDuplications = jest.fn();
159 const onLinePopupToggle = jest.fn();
160 const renderDuplicationPopup = jest.fn();
162 const wrapper = shallowRender({
165 renderDuplicationPopup,
169 const line = mockSourceLine();
171 .find('SnippetViewer')
173 .prop<Function>('loadDuplications')(line);
174 expect(loadDuplications).toHaveBeenCalledWith('a', line);
177 .find('SnippetViewer')
179 .prop<Function>('handleLinePopupToggle')({ line: 13, name: 'foo' });
180 expect(onLinePopupToggle).toHaveBeenCalledWith({ component: 'a', line: 13, name: 'foo' });
183 .find('SnippetViewer')
185 .prop<Function>('renderDuplicationPopup')(1, 13);
186 expect(renderDuplicationPopup).toHaveBeenCalledWith(
187 mockSourceViewerFile({ key: 'a', path: 'a' }),
193 describe('getNodes', () => {
194 const snippetGroup: T.SnippetGroup = {
195 component: mockSourceViewerFile(),
199 const wrapper = mount<ComponentSourceSnippetViewer>(
200 <ComponentSourceSnippetViewer
201 branchLike={mockMainBranch()}
202 duplications={undefined}
203 duplicationsByLine={undefined}
204 highlightedLocationMessage={{ index: 0, text: '' }}
208 linePopup={undefined}
209 loadDuplications={jest.fn()}
211 onIssueChange={jest.fn()}
212 onIssuePopupToggle={jest.fn()}
213 onLinePopupToggle={jest.fn()}
214 onLocationSelect={jest.fn()}
215 renderDuplicationPopup={jest.fn()}
217 snippetGroup={snippetGroup}
221 it('should return undefined if any node is missing', async () => {
222 await waitAndUpdate(wrapper);
223 const rootNode = wrapper.instance().rootNodeRef;
224 mockDom(rootNode.current!);
225 expect(wrapper.instance().getNodes(0)).toBeUndefined();
226 expect(wrapper.instance().getNodes(1)).toBeUndefined();
227 expect(wrapper.instance().getNodes(2)).toBeUndefined();
230 it('should return elements if dom is correct', async () => {
231 await waitAndUpdate(wrapper);
232 const rootNode = wrapper.instance().rootNodeRef;
233 mockDom(rootNode.current!);
234 expect(wrapper.instance().getNodes(3)).not.toBeUndefined();
238 describe('getHeight', () => {
239 jest.useFakeTimers();
241 const snippetGroup: T.SnippetGroup = {
242 component: mockSourceViewerFile(),
246 const wrapper = mount<ComponentSourceSnippetViewer>(
247 <ComponentSourceSnippetViewer
248 branchLike={mockMainBranch()}
249 duplications={undefined}
250 duplicationsByLine={undefined}
251 highlightedLocationMessage={{ index: 0, text: '' }}
255 linePopup={undefined}
256 loadDuplications={jest.fn()}
258 onIssueChange={jest.fn()}
259 onIssuePopupToggle={jest.fn()}
260 onLinePopupToggle={jest.fn()}
261 onLocationSelect={jest.fn()}
262 renderDuplicationPopup={jest.fn()}
264 snippetGroup={snippetGroup}
268 it('should set maxHeight to current height', async () => {
269 await waitAndUpdate(wrapper);
271 const nodes = mockDomForSizes(wrapper, { wrapperHeight: 42, tableHeight: 68 });
272 wrapper.instance().setMaxHeight(0);
274 expect(nodes.wrapper.getAttribute('style')).toBe('max-height: 88px;');
275 expect(nodes.table.getAttribute('style')).toBeNull();
278 it('should set margin and then maxHeight for a nice upwards animation', async () => {
279 await waitAndUpdate(wrapper);
281 const nodes = mockDomForSizes(wrapper, { wrapperHeight: 42, tableHeight: 68 });
282 wrapper.instance().setMaxHeight(0, undefined, true);
284 expect(nodes.wrapper.getAttribute('style')).toBeNull();
285 expect(nodes.table.getAttribute('style')).toBe('transition: none; margin-top: -26px;');
289 expect(nodes.wrapper.getAttribute('style')).toBe('max-height: 88px;');
290 expect(nodes.table.getAttribute('style')).toBe('margin-top: 0px;');
294 function shallowRender(props: Partial<ComponentSourceSnippetViewer['props']> = {}) {
295 const snippetGroup: T.SnippetGroup = {
296 component: mockSourceViewerFile(),
300 return shallow<ComponentSourceSnippetViewer>(
301 <ComponentSourceSnippetViewer
302 branchLike={mockMainBranch()}
303 duplications={undefined}
304 duplicationsByLine={undefined}
305 highlightedLocationMessage={{ index: 0, text: '' }}
309 linePopup={undefined}
310 loadDuplications={jest.fn()}
312 onIssueChange={jest.fn()}
313 onIssuePopupToggle={jest.fn()}
314 onLinePopupToggle={jest.fn()}
315 onLocationSelect={jest.fn()}
316 renderDuplicationPopup={jest.fn()}
318 snippetGroup={snippetGroup}
324 function mockDom(refNode: HTMLDivElement) {
325 refNode.querySelector = jest.fn(query => {
326 const index = query.split('-').pop();
332 return mount(<div />).getDOMNode();
336 <div className="snippet" />
342 <div className="snippet">
353 function mockDomForSizes(
354 componentWrapper: ReactWrapper<{}, {}, ComponentSourceSnippetViewer>,
355 { wrapperHeight = 0, tableHeight = 0 }
357 const wrapper = mount(<div className="snippet" />).getDOMNode();
358 wrapper.getBoundingClientRect = jest.fn().mockReturnValue({ height: wrapperHeight });
359 const table = mount(<div />).getDOMNode();
360 table.getBoundingClientRect = jest.fn().mockReturnValue({ height: tableHeight });
361 componentWrapper.instance().getNodes = jest.fn().mockReturnValue({
365 return { wrapper, table };