* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { cloneDeep } from 'lodash';
+import { cloneDeep, keyBy, range, times } from 'lodash';
+import {
+ mockSnippetsByComponent,
+ mockSourceLine,
+ mockSourceViewerFile
+} from '../../helpers/mocks/sources';
import { RequestData } from '../../helpers/request';
import { getStandards } from '../../helpers/security-standard';
-import { mockPaging } from '../../helpers/testMocks';
-import { RawFacet, RawIssuesResponse, ReferencedComponent } from '../../types/issues';
+import { mockPaging, mockRawIssue, mockRuleDetails } from '../../helpers/testMocks';
+import { BranchParameters } from '../../types/branch-like';
+import { RawFacet, RawIssue, RawIssuesResponse, ReferencedComponent } from '../../types/issues';
import { Standards } from '../../types/security';
-import { searchIssues } from '../issues';
+import {
+ Dict,
+ RuleActivation,
+ RuleDescriptionSections,
+ RuleDetails,
+ SnippetsByComponent,
+ SourceViewerFile
+} from '../../types/types';
+import { getComponentForSourceViewer, getSources } from '../components';
+import { getIssueFlowSnippets, searchIssues } from '../issues';
+import { getRuleDetails } from '../rules';
function mockReferenceComponent(override?: Partial<ReferencedComponent>) {
return {
};
}
+interface IssueData {
+ issue: RawIssue;
+ snippets: Dict<SnippetsByComponent>;
+}
+
export default class IssuesServiceMock {
isAdmin = false;
standards?: Standards;
+ sourceViewerFiles: SourceViewerFile[];
+ list: IssueData[];
constructor() {
- (searchIssues as jest.Mock).mockImplementation(this.listHandler);
- }
-
- reset() {
- this.setIsAdmin(false);
+ this.sourceViewerFiles = [
+ mockSourceViewerFile('file.foo', 'project'),
+ mockSourceViewerFile('file.bar', 'project')
+ ];
+ this.list = [
+ {
+ issue: mockRawIssue(false, {
+ key: 'issue0',
+ component: 'project:file.foo',
+ message: 'Issue on file',
+ rule: 'simpleRuleId',
+ textRange: undefined,
+ line: undefined
+ }),
+ snippets: {}
+ },
+ {
+ issue: mockRawIssue(false, {
+ key: 'issue1',
+ component: 'project:file.foo',
+ message: 'Fix this',
+ rule: 'simpleRuleId',
+ textRange: {
+ startLine: 10,
+ endLine: 10,
+ startOffset: 0,
+ endOffset: 2
+ },
+ flows: [
+ {
+ locations: [
+ {
+ component: 'project:file.foo',
+ textRange: {
+ startLine: 1,
+ endLine: 1,
+ startOffset: 0,
+ endOffset: 1
+ }
+ }
+ ]
+ },
+ {
+ locations: [
+ {
+ component: 'project:file.bar',
+ textRange: {
+ startLine: 20,
+ endLine: 20,
+ startOffset: 0,
+ endOffset: 1
+ }
+ }
+ ]
+ }
+ ]
+ }),
+ snippets: keyBy(
+ [
+ mockSnippetsByComponent(
+ 'file.foo',
+ 'project',
+ times(40, i => i + 1)
+ ),
+ mockSnippetsByComponent(
+ 'file.bar',
+ 'project',
+ times(40, i => i + 1)
+ )
+ ],
+ 'component.key'
+ )
+ },
+ {
+ issue: mockRawIssue(false, {
+ key: 'issue2',
+ component: 'project:file.bar',
+ message: 'Fix that',
+ rule: 'advancedRuleId',
+ textRange: {
+ startLine: 25,
+ endLine: 25,
+ startOffset: 0,
+ endOffset: 1
+ }
+ }),
+ snippets: keyBy(
+ [
+ mockSnippetsByComponent(
+ 'file.bar',
+ 'project',
+ times(40, i => i + 20)
+ )
+ ],
+ 'component.key'
+ )
+ }
+ ];
+ (searchIssues as jest.Mock).mockImplementation(this.handleSearchIssues);
+ (getRuleDetails as jest.Mock).mockImplementation(this.handleGetRuleDetails);
+ (getIssueFlowSnippets as jest.Mock).mockImplementation(this.handleGetIssueFlowSnippets);
+ (getSources as jest.Mock).mockImplementation(this.handleGetSources);
+ (getComponentForSourceViewer as jest.Mock).mockImplementation(
+ this.handleGetComponentForSourceViewer
+ );
}
async getStandards(): Promise<Standards> {
this.isAdmin = isAdmin;
}
- listHandler = (query: RequestData): Promise<RawIssuesResponse> => {
+ handleGetSources = (data: { key: string; from?: number; to?: number } & BranchParameters) => {
+ return this.reply(range(data.from || 1, data.to || 10).map(line => mockSourceLine({ line })));
+ };
+
+ handleGetComponentForSourceViewer = (data: { component: string } & BranchParameters) => {
+ const file = this.sourceViewerFiles.find(f => f.key === data.component);
+ if (file === undefined) {
+ return Promise.reject({
+ errors: [{ msg: `No source file has been found for id ${data.component}` }]
+ });
+ }
+
+ return this.reply(file);
+ };
+
+ handleGetIssueFlowSnippets = (issueKey: string): Promise<Dict<SnippetsByComponent>> => {
+ const issue = this.list.find(i => i.issue.key === issueKey);
+ if (issue === undefined) {
+ return Promise.reject({ errors: [{ msg: `No issue has been found for id ${issueKey}` }] });
+ }
+ return this.reply(issue.snippets);
+ };
+
+ handleGetRuleDetails = (parameters: {
+ actives?: boolean;
+ key: string;
+ }): Promise<{ actives?: RuleActivation[]; rule: RuleDetails }> => {
+ if (parameters.key === 'advancedRuleId') {
+ return this.reply({
+ rule: mockRuleDetails({
+ key: parameters.key,
+ name: 'Advanced rule',
+ descriptionSections: [
+ { key: RuleDescriptionSections.INTRODUCTION, content: '<h1>Into</h1>' },
+ { key: RuleDescriptionSections.ROOT_CAUSE, content: '<h1>Because</h1>' },
+ { key: RuleDescriptionSections.HOW_TO_FIX, content: '<h1>Fix with</h1>' },
+ { key: RuleDescriptionSections.RESOURCES, content: '<h1>Link</h1>' }
+ ]
+ })
+ });
+ }
+ return this.reply({
+ rule: mockRuleDetails({
+ key: parameters.key,
+ name: 'Simple rule',
+ htmlNote: '<h1>Note</h1>',
+ descriptionSections: [
+ {
+ key: RuleDescriptionSections.DEFAULT,
+ content: '<h1>Default</h1> Default description'
+ }
+ ]
+ })
+ });
+ };
+
+ handleSearchIssues = (query: RequestData): Promise<RawIssuesResponse> => {
const facets = query.facets.split(',').map((name: string) => {
if (name === 'owaspTop10-2021') {
return this.owasp2021FacetList();
components: [mockReferenceComponent()],
effortTotal: 199629,
facets,
- issues: [],
+ issues: this.list.map(line => line.issue),
languages: [],
paging: mockPaging()
});
getDuplications,
getSources
} from '../../api/components';
-import { mockSourceLine } from '../../helpers/testMocks';
+import { mockSourceLine } from '../../helpers/mocks/sources';
import { BranchParameters } from '../../types/branch-like';
import { Dict } from '../../types/types';
"defaultRemFnType": "CONSTANT_ISSUE",
"descriptionSections": Array [
Object {
- "content": "<b>Why<b/> Because",
+ "content": "<b>Why</b> Because",
"key": "root_cause",
},
],
"defaultRemFnType": "CONSTANT_ISSUE",
"descriptionSections": Array [
Object {
- "content": "<b>Why<b/> Because",
+ "content": "<b>Why</b> Because",
"key": "root_cause",
},
],
"defaultRemFnType": "CONSTANT_ISSUE",
"descriptionSections": Array [
Object {
- "content": "<b>Why<b/> Because",
+ "content": "<b>Why</b> Because",
"key": "root_cause",
},
],
"defaultRemFnType": "CONSTANT_ISSUE",
"descriptionSections": Array [
Object {
- "content": "<b>Why<b/> Because",
+ "content": "<b>Why</b> Because",
"key": "root_cause",
},
],
import AppContainer from '../components/AppContainer';
jest.mock('../../../api/issues');
+jest.mock('../../../api/rules');
+jest.mock('../../../api/components');
let handler: IssuesServiceMock;
-beforeAll(() => {
+beforeEach(() => {
+ window.scrollTo = jest.fn();
handler = new IssuesServiceMock();
});
-afterEach(() => handler.reset());
+it('should open issue and navigate', async () => {
+ const user = userEvent.setup();
+ renderIssueApp();
+ expect(await screen.findByRole('region', { name: 'Fix that' })).toBeInTheDocument();
+ await user.click(screen.getByRole('region', { name: 'Fix that' }));
+ expect(screen.getByRole('heading', { level: 1, name: 'Fix that' })).toBeInTheDocument();
+ expect(screen.getByRole('link', { name: 'advancedRuleId' })).toBeInTheDocument();
+
+ expect(screen.getByRole('button', { name: `issue.tabs.resources` })).toBeInTheDocument();
+ await user.click(screen.getByRole('button', { name: `issue.tabs.resources` }));
+ expect(screen.getByRole('heading', { name: 'Link' })).toBeInTheDocument();
+
+ expect(screen.getByRole('button', { name: `issue.tabs.how` })).toBeInTheDocument();
+ await user.click(screen.getByRole('button', { name: `issue.tabs.how` }));
+ expect(screen.getByRole('heading', { name: 'Fix with' })).toBeInTheDocument();
+
+ expect(screen.getByRole('button', { name: `issue.tabs.why` })).toBeInTheDocument();
+ await user.click(screen.getByRole('button', { name: `issue.tabs.why` }));
+ expect(screen.getByRole('heading', { name: 'Because' })).toBeInTheDocument();
+
+ await user.keyboard('{ArrowUp}');
+
+ expect(screen.getByRole('heading', { level: 1, name: 'Fix this' })).toBeInTheDocument();
+ expect(screen.getByRole('link', { name: 'simpleRuleId' })).toBeInTheDocument();
+
+ expect(screen.getByRole('button', { name: `issue.tabs.why` })).toBeInTheDocument();
+ await user.click(screen.getByRole('button', { name: `issue.tabs.why` }));
+ expect(screen.getByRole('heading', { name: 'Default' })).toBeInTheDocument();
+
+ await user.keyboard('{ArrowUp}');
+
+ expect(screen.getByRole('heading', { level: 1, name: 'Issue on file' })).toBeInTheDocument();
+ expect(screen.getByRole('link', { name: 'simpleRuleId' })).toBeInTheDocument();
+
+ expect(screen.getByRole('button', { name: `issue.tabs.code` })).toBeInTheDocument();
+ await user.click(screen.getByRole('button', { name: `issue.tabs.code` }));
+ expect(screen.getByRole('region', { name: 'Issue on file' })).toBeInTheDocument();
+ expect(
+ screen.getByRole('row', {
+ name: '2 source_viewer.tooltip.covered import java.util. ArrayList ;'
+ })
+ ).toBeInTheDocument();
+});
it('should support OWASP Top 10 version 2021', async () => {
const user = userEvent.setup();
import { getBranchLikeQuery } from '../../../helpers/branch-like';
import { throwGlobalError } from '../../../helpers/error';
import { translate } from '../../../helpers/l10n';
+import { HttpStatus } from '../../../helpers/request';
import { BranchLike } from '../../../types/branch-like';
import { isFile } from '../../../types/component';
import {
}
} catch (response) {
const rsp = response as Response;
- if (rsp.status !== 403) {
+ if (rsp.status !== HttpStatus.Forbidden) {
throwGlobalError(response);
}
if (this.mounted) {
- this.setState({ loading: false, notAccessible: rsp.status === 403 });
+ this.setState({ loading: false, notAccessible: rsp.status === HttpStatus.Forbidden });
}
}
}
import Issue from '../../../../components/issue/Issue';
import { mockBranch, mockMainBranch } from '../../../../helpers/mocks/branch-like';
import {
- mockFlowLocation,
- mockIssue,
mockSnippetsByComponent,
mockSourceLine,
mockSourceViewerFile
-} from '../../../../helpers/testMocks';
+} from '../../../../helpers/mocks/sources';
+import { mockFlowLocation, mockIssue } from '../../../../helpers/testMocks';
import { waitAndUpdate } from '../../../../helpers/testUtils';
import { SnippetGroup } from '../../../../types/types';
import ComponentSourceSnippetGroupViewer from '../ComponentSourceSnippetGroupViewer';
it('should render correctly with secondary locations', () => {
// issue with secondary locations but no flows
const issue = mockIssue(true, {
+ component: 'project:main.js',
flows: [],
textRange: { startLine: 7, endLine: 7, startOffset: 5, endOffset: 10 }
});
textRange: { startLine: 74, endLine: 74, startOffset: 0, endOffset: 0 }
})
],
- ...mockSnippetsByComponent(issue.component, [
+ ...mockSnippetsByComponent('main.js', 'project', [
...range(2, 17),
...range(29, 39),
...range(69, 79)
it('should render correctly with flows', () => {
// issue with flows but no secondary locations
const issue = mockIssue(true, {
+ component: 'project:main.js',
secondaryLocations: [],
textRange: { startLine: 7, endLine: 7, startOffset: 5, endOffset: 10 }
});
textRange: { startLine: 74, endLine: 74, startOffset: 0, endOffset: 0 }
})
],
- ...mockSnippetsByComponent(issue.component, [
+ ...mockSnippetsByComponent('main.js', 'project', [
...range(2, 17),
...range(29, 39),
...range(69, 79)
it('should render file-level issue correctly', () => {
// issue with secondary locations and no primary location
const issue = mockIssue(true, {
+ component: 'project:main.js',
flows: [],
textRange: undefined
});
textRange: { startLine: 34, endLine: 34, startOffset: 0, endOffset: 0 }
})
],
- ...mockSnippetsByComponent(issue.component, range(29, 39))
+ ...mockSnippetsByComponent('main.js', 'project', range(29, 39))
}
});
it('should expand block', async () => {
(getSources as jest.Mock).mockResolvedValueOnce(
- Object.values(mockSnippetsByComponent('a', range(6, 59)).sources)
+ Object.values(mockSnippetsByComponent('a', 'project', range(6, 59)).sources)
);
const issue = mockIssue(true, {
textRange: { startLine: 74, endLine: 74, startOffset: 5, endOffset: 10 }
textRange: { startLine: 107, endLine: 107, startOffset: 0, endOffset: 0 }
})
],
- ...mockSnippetsByComponent('a', [...range(69, 83), ...range(102, 112)])
+ ...mockSnippetsByComponent('a', 'project', [...range(69, 83), ...range(102, 112)])
};
const wrapper = shallowRender({ issue, snippetGroup });
wrapper.instance().expandBlock(0, 'up');
await waitAndUpdate(wrapper);
- expect(getSources).toHaveBeenCalledWith({ from: 9, key: 'a', to: 68 });
+ expect(getSources).toHaveBeenCalledWith({ from: 9, key: 'project:a', to: 68 });
expect(wrapper.state('snippets')).toHaveLength(2);
expect(wrapper.state('snippets')[0]).toEqual({ index: 0, start: 19, end: 83 });
expect(Object.keys(wrapper.state('additionalLines'))).toHaveLength(53);
it('should expand full component', async () => {
(getSources as jest.Mock).mockResolvedValueOnce(
- Object.values(mockSnippetsByComponent('a', times(14)).sources)
+ Object.values(mockSnippetsByComponent('a', 'project', times(14)).sources)
);
const snippetGroup: SnippetGroup = {
locations: [
textRange: { startLine: 12, endLine: 12, startOffset: 0, endOffset: 0 }
})
],
- ...mockSnippetsByComponent('a', [1, 2, 3, 4, 5, 10, 11, 12, 13, 14])
+ ...mockSnippetsByComponent('a', 'project', [1, 2, 3, 4, 5, 10, 11, 12, 13, 14])
};
const wrapper = shallowRender({ snippetGroup });
wrapper.instance().expandComponent();
await waitAndUpdate(wrapper);
- expect(getSources).toHaveBeenCalledWith({ key: 'a' });
+ expect(getSources).toHaveBeenCalledWith({ key: 'project:a' });
expect(wrapper.state('snippets')).toHaveLength(1);
expect(wrapper.state('snippets')[0]).toEqual({ index: -1, start: 0, end: 13 });
});
it('should get the right branch when expanding', async () => {
(getSources as jest.Mock).mockResolvedValueOnce(
Object.values(
- mockSnippetsByComponent('a', [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]).sources
+ mockSnippetsByComponent('a', 'project', [5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17])
+ .sources
)
);
const snippetGroup: SnippetGroup = {
locations: [mockFlowLocation()],
- ...mockSnippetsByComponent('a', [1, 2, 3, 4, 5, 6, 7])
+ ...mockSnippetsByComponent('a', 'project', [1, 2, 3, 4, 5, 6, 7])
};
const wrapper = shallowRender({
wrapper.instance().expandBlock(0, 'down');
await waitAndUpdate(wrapper);
- expect(getSources).toHaveBeenCalledWith({ branch: 'asdf', from: 8, key: 'a', to: 67 });
+ expect(getSources).toHaveBeenCalledWith({ branch: 'asdf', from: 8, key: 'project:a', to: 67 });
});
it('should handle correctly open/close issue', () => {
const snippetGroup: SnippetGroup = {
locations: [
mockFlowLocation({
- component: 'a',
+ component: 'my-project:foo/bar.ts',
textRange: { startLine: 34, endLine: 34, startOffset: 0, endOffset: 0 }
}),
mockFlowLocation({
- component: 'a',
+ component: 'my-project:foo/bar.ts',
textRange: { startLine: 54, endLine: 54, startOffset: 0, endOffset: 0 }
})
],
- ...mockSnippetsByComponent('a', [32, 33, 34, 35, 36, 52, 53, 54, 55, 56])
+ ...mockSnippetsByComponent('foo/bar.ts', 'my-project', [32, 33, 34, 35, 36, 52, 53, 54, 55, 56])
};
const loadDuplications = jest.fn();
const renderDuplicationPopup = jest.fn();
.find('SnippetViewer')
.first()
.prop<Function>('loadDuplications')(line);
- expect(loadDuplications).toHaveBeenCalledWith('a', line);
+ expect(loadDuplications).toHaveBeenCalledWith('my-project:foo/bar.ts', line);
wrapper
.find('SnippetViewer')
.first()
.prop<Function>('renderDuplicationPopup')(1, 13);
expect(renderDuplicationPopup).toHaveBeenCalledWith(
- mockSourceViewerFile({ key: 'a', path: 'a' }),
+ mockSourceViewerFile('foo/bar.ts', 'my-project'),
1,
13
);
import { getDuplications } from '../../../../api/components';
import { getIssueFlowSnippets } from '../../../../api/issues';
import {
- mockFlowLocation,
- mockIssue,
mockSnippetsByComponent,
mockSourceLine,
mockSourceViewerFile
-} from '../../../../helpers/testMocks';
+} from '../../../../helpers/mocks/sources';
+import { mockFlowLocation, mockIssue } from '../../../../helpers/testMocks';
import { waitAndUpdate } from '../../../../helpers/testUtils';
import CrossComponentSourceViewerWrapper from '../CrossComponentSourceViewerWrapper';
jest.mock('../../../../api/issues', () => {
- const { mockSnippetsByComponent } = jest.requireActual('../../../../helpers/testMocks');
+ const { mockSnippetsByComponent } = jest.requireActual('../../../../helpers/mocks/sources');
return {
getIssueFlowSnippets: jest.fn().mockResolvedValue({ 'main.js': mockSnippetsByComponent() })
};
import { mount, 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, mockSourceLine, mockSourceViewerFile } from '../../../../helpers/testMocks';
+import { mockIssue } from '../../../../helpers/testMocks';
import SnippetViewer from '../SnippetViewer';
jest.mock('../../../../helpers/scrolling', () => ({
});
it('should render correctly when at the bottom of the file', () => {
- const component = mockSourceViewerFile({ measures: { lines: '14' } });
+ const component = mockSourceViewerFile('foo/bar.ts', 'my-project', { measures: { lines: '14' } });
const snippet = range(10, 14).map(line => mockSourceLine({ line }));
const wrapper = shallowRender({
component,
onExpand={[Function]}
sourceViewerFile={
Object {
- "key": "foo",
+ "canMarkAsFavorite": true,
+ "fav": false,
+ "key": "project:foo/bar.ts",
+ "longName": "foo/bar.ts",
"measures": Object {
"coverage": "85.2",
"duplicationDensity": "1.0",
"issues": "12",
"lines": "56",
},
+ "name": "foo/bar.ts",
"path": "foo/bar.ts",
- "project": "my-project",
+ "project": "project",
"projectName": "MyProject",
"q": "FIL",
"uuid": "foo-bar",
Object {
"branchLike": undefined,
"file": Object {
- "key": "main.js",
+ "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": "my-project",
+ "project": "project",
"projectName": "MyProject",
"q": "FIL",
"uuid": "foo-bar",
>
<ComponentSourceSnippetGroupViewer
duplicationsByLine={Object {}}
- isLastOccurenceOfPrimaryComponent={true}
+ isLastOccurenceOfPrimaryComponent={false}
issue={
Object {
"actions": Array [],
snippetGroup={
Object {
"component": Object {
- "key": "main.js",
+ "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": "my-project",
+ "project": "project",
"projectName": "MyProject",
"q": "FIL",
"uuid": "foo-bar",
Object {
"branchLike": undefined,
"file": Object {
- "key": "main.js",
+ "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": "my-project",
+ "project": "project",
"projectName": "MyProject",
"q": "FIL",
"uuid": "foo-bar",
snippetGroup={
Object {
"component": Object {
- "key": "main.js",
+ "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": "my-project",
+ "project": "project",
"projectName": "MyProject",
"q": "FIL",
"uuid": "foo-bar",
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import {
- mockFlowLocation,
- mockIssue,
- mockSnippetsByComponent
-} from '../../../../helpers/testMocks';
+import { mockSnippetsByComponent } from '../../../../helpers/mocks/sources';
+import { mockFlowLocation, mockIssue } from '../../../../helpers/testMocks';
import { createSnippets, expandSnippet, groupLocationsByComponent } from '../utils';
describe('groupLocationsByComponent', () => {
textRange: { startLine: 24, startOffset: 1, endLine: 24, endOffset: 2 }
})
],
- { 'main.js': mockSnippetsByComponent('main.js', [14, 15, 16, 17, 18, 22, 23, 24, 25, 26]) }
+ {
+ 'main.js': mockSnippetsByComponent('main.js', 'project', [
+ 14,
+ 15,
+ 16,
+ 17,
+ 18,
+ 22,
+ 23,
+ 24,
+ 25,
+ 26
+ ])
+ }
);
expect(results).toHaveLength(1);
})
],
{
- 'A.js': mockSnippetsByComponent('A.js', [13, 14, 15, 16, 17, 18]),
- 'B.js': mockSnippetsByComponent('B.js', [14, 15, 16, 17, 18])
+ 'A.js': mockSnippetsByComponent('A.js', 'project', [13, 14, 15, 16, 17, 18]),
+ 'B.js': mockSnippetsByComponent('B.js', 'project', [14, 15, 16, 17, 18])
}
);
expect(results).toHaveLength(3);
- expect(results[0].component.key).toBe('A.js');
- expect(results[1].component.key).toBe('B.js');
- expect(results[2].component.key).toBe('A.js');
+ expect(results[0].component.key).toBe('project:A.js');
+ expect(results[1].component.key).toBe('project:B.js');
+ expect(results[2].component.key).toBe('project:A.js');
expect(results[0].locations).toHaveLength(1);
expect(results[1].locations).toHaveLength(1);
expect(results[2].locations).toHaveLength(1);
import { mockBranch } from '../../../../helpers/mocks/branch-like';
import { mockComponent } from '../../../../helpers/mocks/component';
import { mockHotspot, mockHotspotComponent } from '../../../../helpers/mocks/security-hotspots';
-import { mockFlowLocation, mockSourceLine } from '../../../../helpers/testMocks';
+import { mockSourceLine } from '../../../../helpers/mocks/sources';
+import { mockFlowLocation } from '../../../../helpers/testMocks';
import { waitAndUpdate } from '../../../../helpers/testUtils';
import { ComponentQualifier } from '../../../../types/component';
import HotspotSnippetContainer from '../HotspotSnippetContainer';
import { mockMainBranch } from '../../../../helpers/mocks/branch-like';
import { mockComponent } from '../../../../helpers/mocks/component';
import { mockHotspot } from '../../../../helpers/mocks/security-hotspots';
+import { mockSourceLine, mockSourceViewerFile } from '../../../../helpers/mocks/sources';
import { scrollToElement } from '../../../../helpers/scrolling';
-import { mockSourceLine, mockSourceViewerFile } from '../../../../helpers/testMocks';
import SnippetViewer from '../../../issues/crossComponentSourceViewer/SnippetViewer';
import HotspotSnippetContainerRenderer, {
animateExpansion,
<SnippetViewer
component={
Object {
- "key": "foo",
+ "canMarkAsFavorite": true,
+ "fav": false,
+ "key": "project:foo/bar.ts",
+ "longName": "foo/bar.ts",
"measures": Object {
"coverage": "85.2",
"duplicationDensity": "1.0",
"issues": "12",
"lines": "56",
},
+ "name": "foo/bar.ts",
"path": "foo/bar.ts",
- "project": "my-project",
+ "project": "project",
"projectName": "MyProject",
"q": "FIL",
"uuid": "foo-bar",
import * as React from 'react';
import { getComponentData, getComponentForSourceViewer, getSources } from '../../../api/components';
import { mockMainBranch } from '../../../helpers/mocks/branch-like';
-import { mockIssue, mockSourceLine, mockSourceViewerFile } from '../../../helpers/testMocks';
+import { mockSourceLine, mockSourceViewerFile } from '../../../helpers/mocks/sources';
+import { mockIssue } from '../../../helpers/testMocks';
import { waitAndUpdate } from '../../../helpers/testUtils';
import defaultLoadIssues from '../helpers/loadIssues';
import SourceViewerBase from '../SourceViewerBase';
import { shallow } from 'enzyme';
import * as React from 'react';
import { mockBranch } from '../../../helpers/mocks/branch-like';
-import { mockIssue, mockSourceLine } from '../../../helpers/testMocks';
+import { mockSourceLine } from '../../../helpers/mocks/sources';
+import { mockIssue } from '../../../helpers/testMocks';
import { MetricKey } from '../../../types/metrics';
import Line from '../components/Line';
import SourceViewerCode from '../SourceViewerCode';
import { shallow } from 'enzyme';
import * as React from 'react';
import { mockMainBranch } from '../../../helpers/mocks/branch-like';
-import { mockSourceViewerFile } from '../../../helpers/testMocks';
+import { mockSourceViewerFile } from '../../../helpers/mocks/sources';
import { ComponentQualifier } from '../../../types/component';
import { MetricKey } from '../../../types/metrics';
import { Measure } from '../../../types/types';
expect(
shallowRender({
showMeasures: true,
- sourceViewerFile: mockSourceViewerFile({
+ sourceViewerFile: mockSourceViewerFile('foo/bar.ts', 'my-project', {
q: ComponentQualifier.TestFile,
measures: { tests: '12' }
})
import { shallow } from 'enzyme';
import * as React from 'react';
import { mockMainBranch } from '../../../helpers/mocks/branch-like';
-import { mockSourceViewerFile } from '../../../helpers/testMocks';
+import { mockSourceViewerFile } from '../../../helpers/mocks/sources';
import { ComponentQualifier } from '../../../types/component';
import SourceViewerHeaderSlim, { Props } from '../SourceViewerHeaderSlim';
expect(shallowRender({ linkToProject: false })).toMatchSnapshot('no link to project');
expect(shallowRender({ displayProjectName: false })).toMatchSnapshot('no project name');
expect(
- shallowRender({ sourceViewerFile: mockSourceViewerFile({ q: ComponentQualifier.Project }) })
+ shallowRender({
+ sourceViewerFile: mockSourceViewerFile('foo/bar.ts', 'my-project', {
+ q: ComponentQualifier.Project
+ })
+ })
).toMatchSnapshot('project root');
});
it('should render correctly for subproject', () => {
expect(
shallowRender({
- sourceViewerFile: mockSourceViewerFile({ subProject: 'foo', subProjectName: 'Foo' })
+ sourceViewerFile: mockSourceViewerFile('foo/bar.ts', 'my-project', {
+ subProject: 'foo',
+ subProjectName: 'Foo'
+ })
})
).toMatchSnapshot();
});
branchLike={mockMainBranch()}
expandable={true}
onExpand={jest.fn()}
- sourceViewerFile={mockSourceViewerFile()}
+ sourceViewerFile={mockSourceViewerFile('foo/bar.ts', 'my-project')}
{...props}
/>
);
"name": "master",
},
"file": Object {
- "key": "foo",
+ "canMarkAsFavorite": true,
+ "fav": false,
+ "key": "project:foo/bar.ts",
"leakPeriodDate": "2018-06-20T17:12:19+0200",
+ "longName": "foo/bar.ts",
"measures": Object {
"coverage": "85.2",
"duplicationDensity": "1.0",
"issues": "12",
"lines": "56",
},
+ "name": "foo/bar.ts",
"path": "foo/bar.ts",
- "project": "my-project",
+ "project": "project",
"projectName": "MyProject",
"q": "FIL",
"uuid": "foo-bar",
>
<a
className="link-with-icon"
- href="/dashboard?id=my-project"
+ href="/dashboard?id=project"
>
<QualifierIcon
qualifier="TRK"
Object {
"pathname": "/code",
"query": Object {
- "id": "my-project",
+ "id": "project",
"line": undefined,
- "selected": "foo",
+ "selected": "project:foo/bar.ts",
},
}
}
<li>
<a
className="js-raw-source"
- href="/api/sources/raw?key=foo"
+ href="/api/sources/raw?key=project%3Afoo%2Fbar.ts"
rel="noopener noreferrer"
target="_blank"
>
"query": Object {
"id": "my-project",
"line": undefined,
- "selected": "foo",
+ "selected": "my-project:foo/bar.ts",
},
}
}
<li>
<a
className="js-raw-source"
- href="/api/sources/raw?key=foo"
+ href="/api/sources/raw?key=my-project%3Afoo%2Fbar.ts"
rel="noopener noreferrer"
target="_blank"
>
>
<a
className="link-with-icon"
- href="/dashboard?id=my-project"
+ href="/dashboard?id=project"
>
<QualifierIcon
qualifier="TRK"
"pathname": "/project/issues",
"query": Object {
"files": "foo/bar.ts",
- "id": "my-project",
+ "id": "project",
"resolved": "false",
"types": "BUG",
},
"pathname": "/project/issues",
"query": Object {
"files": "foo/bar.ts",
- "id": "my-project",
+ "id": "project",
"resolved": "false",
"types": "VULNERABILITY",
},
"pathname": "/project/issues",
"query": Object {
"files": "foo/bar.ts",
- "id": "my-project",
+ "id": "project",
"resolved": "false",
"types": "CODE_SMELL",
},
"pathname": "/project/issues",
"query": Object {
"files": "foo/bar.ts",
- "id": "my-project",
+ "id": "project",
"resolved": "false",
"types": "SECURITY_HOTSPOT",
},
Object {
"pathname": "/code",
"query": Object {
- "id": "my-project",
+ "id": "project",
"line": undefined,
- "selected": "foo",
+ "selected": "project:foo/bar.ts",
},
}
}
<li>
<a
className="js-raw-source"
- href="/api/sources/raw?key=foo"
+ href="/api/sources/raw?key=project%3Afoo%2Fbar.ts"
rel="noopener noreferrer"
target="_blank"
>
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import { mockIssue, mockSourceLine } from '../../../../helpers/testMocks';
+import { mockSourceLine } from '../../../../helpers/mocks/sources';
+import { mockIssue } from '../../../../helpers/testMocks';
import Line from '../Line';
it('should render correctly for last, new, and highlighted lines', () => {
*/
import { shallow } from 'enzyme';
import * as React from 'react';
-import { mockSourceLine } from '../../../../helpers/testMocks';
+import { mockSourceLine } from '../../../../helpers/mocks/sources';
import LineCode from '../LineCode';
it('render code', () => {
import { shallow } from 'enzyme';
import * as React from 'react';
import { mockBranch } from '../../../../helpers/mocks/branch-like';
-import { mockIssue, mockSourceLine } from '../../../../helpers/testMocks';
+import { mockSourceLine } from '../../../../helpers/mocks/sources';
+import { mockIssue } from '../../../../helpers/testMocks';
import LineIssuesList, { LineIssuesListProps } from '../LineIssuesList';
it('should render issues', () => {
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { mockFlowLocation, mockSourceLine } from '../../../../helpers/testMocks';
+import { mockSourceLine } from '../../../../helpers/mocks/sources';
+import { mockFlowLocation } from '../../../../helpers/testMocks';
import { getLinearLocations, getSecondaryIssueLocationsForLine } from '../issueLocations';
describe('getSecondaryIssueLocationsForLine', () => {
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2022 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+import { ComponentQualifier } from '../../types/component';
+import { SnippetsByComponent, SourceLine, SourceViewerFile } from '../../types/types';
+
+export function mockSourceViewerFile(
+ name = 'foo/bar.ts',
+ project = 'project',
+ override?: Partial<SourceViewerFile>
+): SourceViewerFile {
+ return {
+ measures: {
+ coverage: '85.2',
+ duplicationDensity: '1.0',
+ issues: '12',
+ lines: '56'
+ },
+ project,
+ projectName: 'MyProject',
+ q: ComponentQualifier.File,
+ uuid: 'foo-bar',
+ key: `${project}:${name}`,
+ path: name,
+ name,
+ longName: name,
+ fav: false,
+ canMarkAsFavorite: true,
+ ...override
+ };
+}
+
+export function mockSourceLine(overrides: Partial<SourceLine> = {}): SourceLine {
+ return {
+ line: 16,
+ code: '<span class="k">import</span> java.util.<span class="sym-9 sym">ArrayList</span>;',
+ coverageStatus: 'covered',
+ coveredConditions: 2,
+ scmRevision: '80f564becc0c0a1c9abaa006eca83a4fd278c3f0',
+ scmAuthor: 'simon.brandhof@sonarsource.com',
+ scmDate: '2018-12-11T10:48:39+0100',
+ duplicated: false,
+ isNew: true,
+ ...overrides
+ };
+}
+
+export function mockSnippetsByComponent(
+ file = 'main.js',
+ project = 'project',
+ lines: number[] = [16]
+): SnippetsByComponent {
+ const sources = lines.reduce((lines: { [key: number]: SourceLine }, line) => {
+ lines[line] = mockSourceLine({ line });
+ return lines;
+ }, {});
+ return {
+ component: mockSourceViewerFile(file, project),
+ sources
+ };
+}
import { Exporter, Profile } from '../apps/quality-profiles/types';
import { AppState } from '../types/appstate';
import { RuleRepository } from '../types/coding-rules';
-import { ComponentQualifier } from '../types/component';
import { EditionKey } from '../types/editions';
-import { RawIssue } from '../types/issues';
+import { IssueType, RawIssue } from '../types/issues';
import { Language } from '../types/languages';
import { DumpStatus, DumpTask } from '../types/project-dump';
import { TaskStatuses } from '../types/tasks';
RuleDescriptionSections,
RuleDetails,
RuleParameter,
- SnippetsByComponent,
- SourceLine,
- SourceViewerFile,
SysInfoBase,
SysInfoCluster,
SysInfoStandalone
};
}
-export function mockSnippetsByComponent(
- component = 'main.js',
- lines: number[] = [16]
-): SnippetsByComponent {
- const sources = lines.reduce((lines: { [key: number]: SourceLine }, line) => {
- lines[line] = mockSourceLine({ line });
- return lines;
- }, {});
- return {
- component: mockSourceViewerFile({
- key: component,
- path: component
- }),
- sources
- };
-}
-
-export function mockSourceLine(overrides: Partial<SourceLine> = {}): SourceLine {
- return {
- line: 16,
- code: '<span class="k">import</span> java.util.<span class="sym-9 sym">ArrayList</span>;',
- coverageStatus: 'covered',
- coveredConditions: 2,
- scmRevision: '80f564becc0c0a1c9abaa006eca83a4fd278c3f0',
- scmAuthor: 'simon.brandhof@sonarsource.com',
- scmDate: '2018-12-11T10:48:39+0100',
- duplicated: false,
- isNew: true,
- ...overrides
- };
-}
-
export function mockCurrentUser(overrides: Partial<CurrentUser> = {}): CurrentUser {
return {
isLoggedIn: false,
export function mockRawIssue(withLocations = false, overrides: Partial<RawIssue> = {}): RawIssue {
const rawIssue: RawIssue = {
+ actions: [],
component: 'main.js',
key: 'AVsae-CQS-9G3txfbFN2',
line: 25,
severity: 'MAJOR',
status: 'OPEN',
textRange: { startLine: 25, endLine: 26, startOffset: 0, endOffset: 15 },
+ type: IssueType.CodeSmell,
...overrides
};
if (withLocations) {
const loc = mockFlowLocation;
- rawIssue.flows = [{ locations: [loc(), loc()] }];
+ rawIssue.flows = [
+ {
+ locations: [
+ loc({ component: overrides.component }),
+ loc({ component: overrides.component })
+ ]
+ }
+ ];
}
return {
descriptionSections: [
{
key: RuleDescriptionSections.ROOT_CAUSE,
- content: '<b>Why<b/> Because'
+ content: '<b>Why</b> Because'
}
],
htmlDesc: '',
};
}
-export function mockSourceViewerFile(overrides: Partial<SourceViewerFile> = {}): SourceViewerFile {
- return {
- key: 'foo',
- measures: {
- coverage: '85.2',
- duplicationDensity: '1.0',
- issues: '12',
- lines: '56'
- },
- path: 'foo/bar.ts',
- project: 'my-project',
- projectName: 'MyProject',
- q: ComponentQualifier.File,
- uuid: 'foo-bar',
- ...overrides
- };
-}
-
export function mockStandaloneSysInfo(overrides: Partial<any> = {}): SysInfoStandalone {
const baseInfo = mockBaseSysInfo(overrides);
return {
}
export interface RawIssue {
+ actions: string[];
assignee?: string;
author?: string;
comments?: Array<Comment>;
line?: number;
project: string;
rule: string;
+ message?: string;
severity: string;
status: string;
subProject?: string;
textRange?: TextRange;
+ type: IssueType;
}
export interface IssueResponse {
lines?: string;
tests?: string;
};
+ canMarkAsFavorite?: boolean;
path: string;
+ name?: string;
+ longName?: string;
project: string;
projectName: string;
q: ComponentQualifier;