3 * Copyright (C) 2009-2022 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 { shallow } from 'enzyme';
21 import { range } from 'lodash';
22 import * as React from 'react';
23 import { getSources } from '../../../../api/components';
24 import { mockBranch } from '../../../../helpers/mocks/branch-like';
25 import { mockComponent } from '../../../../helpers/mocks/component';
26 import { mockHotspot, mockHotspotComponent } from '../../../../helpers/mocks/security-hotspots';
27 import { mockFlowLocation, mockSourceLine } from '../../../../helpers/testMocks';
28 import { waitAndUpdate } from '../../../../helpers/testUtils';
29 import { ComponentQualifier } from '../../../../types/component';
30 import HotspotSnippetContainer from '../HotspotSnippetContainer';
31 import HotspotSnippetContainerRenderer from '../HotspotSnippetContainerRenderer';
33 jest.mock('../../../../api/components', () => ({
34 getSources: jest.fn().mockResolvedValue([])
37 jest.mock('../../../../helpers/scrolling', () => ({
38 scrollToElement: jest.fn()
41 beforeEach(() => jest.clearAllMocks());
43 const branch = mockBranch();
45 it('should render correctly', () => {
46 expect(shallowRender()).toMatchSnapshot();
49 it('should load sources on mount', async () => {
50 (getSources as jest.Mock).mockResolvedValueOnce(
51 range(1, 25).map(line => mockSourceLine({ line }))
54 const hotspot = mockHotspot({
55 project: mockHotspotComponent({ branch: branch.name, qualifier: ComponentQualifier.Project }),
56 textRange: { startLine: 10, endLine: 11, startOffset: 0, endOffset: 12 },
61 textRange: { startLine: 8, endLine: 8, startOffset: 0, endOffset: 1 }
64 textRange: { startLine: 13, endLine: 13, startOffset: 0, endOffset: 1 }
71 const wrapper = shallowRender({ hotspot });
73 await waitAndUpdate(wrapper);
75 expect(getSources).toBeCalledWith(
76 expect.objectContaining({
77 key: hotspot.component.key,
83 expect(wrapper.state().lastLine).toBeUndefined();
84 expect(wrapper.state().sourceLines).toHaveLength(23);
87 it('should handle load sources failure', async () => {
88 (getSources as jest.Mock).mockRejectedValueOnce(null);
90 const wrapper = shallowRender();
92 await waitAndUpdate(wrapper);
94 expect(getSources).toHaveBeenCalled();
95 expect(wrapper.state().loading).toBe(false);
96 expect(wrapper.state().lastLine).toBeUndefined();
97 expect(wrapper.state().sourceLines).toHaveLength(0);
100 it('should not load sources on mount when the hotspot is not associated to any loc', async () => {
101 const hotspot = mockHotspot({
106 const wrapper = shallowRender({ hotspot });
108 await waitAndUpdate(wrapper);
110 expect(getSources).not.toBeCalled();
111 expect(wrapper.state().lastLine).toBeUndefined();
112 expect(wrapper.state().sourceLines).toHaveLength(0);
115 it('should handle end-of-file on mount', async () => {
116 (getSources as jest.Mock).mockResolvedValueOnce(
117 range(5, 15).map(line => mockSourceLine({ line }))
120 const hotspot = mockHotspot({
121 textRange: { startLine: 10, endLine: 11, startOffset: 0, endOffset: 12 }
124 const wrapper = shallowRender({ hotspot });
126 await waitAndUpdate(wrapper);
128 expect(getSources).toBeCalled();
129 expect(wrapper.state().lastLine).toBe(14);
130 expect(wrapper.state().sourceLines).toHaveLength(10);
133 describe('Expansion', () => {
135 (getSources as jest.Mock).mockResolvedValueOnce(
136 range(10, 32).map(line => mockSourceLine({ line }))
140 const hotspot = mockHotspot({
141 project: mockHotspotComponent({ branch: branch.name, qualifier: ComponentQualifier.Project }),
142 textRange: { startLine: 20, endLine: 21, startOffset: 0, endOffset: 12 }
145 it('up should work', async () => {
146 (getSources as jest.Mock).mockResolvedValueOnce(
147 range(1, 10).map(line => mockSourceLine({ line }))
150 const wrapper = shallowRender({ hotspot });
151 await waitAndUpdate(wrapper);
154 .find(HotspotSnippetContainerRenderer)
156 .onExpandBlock('up');
158 await waitAndUpdate(wrapper);
160 expect(getSources).toBeCalledWith(
161 expect.objectContaining({
165 expect(wrapper.state().sourceLines).toHaveLength(31);
168 it('down should work', async () => {
169 (getSources as jest.Mock).mockResolvedValueOnce(
170 // lastLine + expand + extra for EOF check + range end is excluded
172 range(32, 83).map(line => mockSourceLine({ line }))
175 const wrapper = shallowRender({ hotspot });
176 await waitAndUpdate(wrapper);
179 .find(HotspotSnippetContainerRenderer)
181 .onExpandBlock('down');
183 await waitAndUpdate(wrapper);
185 expect(wrapper.state().lastLine).toBeUndefined();
186 expect(wrapper.state().sourceLines).toHaveLength(72);
189 it('down should work and handle EOF', async () => {
190 (getSources as jest.Mock).mockResolvedValueOnce(
191 // lastLine + expand + extra for EOF check + range end is excluded - 1 to trigger end-of-file
192 // 26 + 50 + 1 + 1 - 1
193 range(27, 77).map(line => mockSourceLine({ line }))
196 const wrapper = shallowRender({ hotspot });
197 await waitAndUpdate(wrapper);
200 .find(HotspotSnippetContainerRenderer)
202 .onExpandBlock('down');
204 await waitAndUpdate(wrapper);
206 expect(wrapper.state().lastLine).toBe(76);
207 expect(wrapper.state().sourceLines).toHaveLength(72);
211 it('should handle symbol click', () => {
212 const wrapper = shallowRender();
213 const symbols = ['symbol'];
215 .find(HotspotSnippetContainerRenderer)
217 .onSymbolClick(symbols);
218 expect(wrapper.state().highlightedSymbols).toBe(symbols);
221 function shallowRender(props?: Partial<HotspotSnippetContainer['props']>) {
222 return shallow<HotspotSnippetContainer>(
223 <HotspotSnippetContainer
225 component={mockComponent()}
226 hotspot={mockHotspot()}
227 onCommentButtonClick={jest.fn()}
228 onLocationSelect={jest.fn()}