Browse Source

SONAR-16303 Add frontend IT for advance rule

tags/9.5.0.56709
Mathieu Suen 1 year ago
parent
commit
aaf931d953
28 changed files with 475 additions and 157 deletions
  1. 190
    11
      server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts
  2. 1
    1
      server/sonar-web/src/main/js/api/mocks/SourceViewerServiceMock.ts
  3. 4
    4
      server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetails-test.tsx.snap
  4. 47
    2
      server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx
  5. 3
    2
      server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewerWrapper.tsx
  6. 23
    20
      server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/ComponentSourceSnippetGroupViewer-test.tsx
  7. 3
    4
      server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/CrossComponentSourceViewerWrapper-test.tsx
  8. 3
    2
      server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/SnippetViewer-test.tsx
  9. 6
    2
      server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/ComponentSourceSnippetGroupViewer-test.tsx.snap
  10. 25
    9
      server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/CrossComponentSourceViewerWrapper-test.tsx.snap
  11. 21
    11
      server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/utils-test.ts
  12. 2
    1
      server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainer-test.tsx
  13. 1
    1
      server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainerRenderer-test.tsx
  14. 6
    2
      server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSnippetContainerRenderer-test.tsx.snap
  15. 2
    1
      server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewerBase-test.tsx
  16. 2
    1
      server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewerCode-test.tsx
  17. 2
    2
      server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewerHeader-test.tsx
  18. 11
    4
      server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewerHeaderSlim-test.tsx
  19. 6
    2
      server/sonar-web/src/main/js/components/SourceViewer/__tests__/__snapshots__/SourceViewerBase-test.tsx.snap
  20. 14
    14
      server/sonar-web/src/main/js/components/SourceViewer/__tests__/__snapshots__/SourceViewerHeader-test.tsx.snap
  21. 2
    1
      server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/Line-test.tsx
  22. 1
    1
      server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineCode-test.tsx
  23. 2
    1
      server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineIssueList-test.tsx
  24. 2
    1
      server/sonar-web/src/main/js/components/SourceViewer/helpers/__tests__/issueLocations-test.ts
  25. 78
    0
      server/sonar-web/src/main/js/helpers/mocks/sources.ts
  26. 12
    57
      server/sonar-web/src/main/js/helpers/testMocks.ts
  27. 3
    0
      server/sonar-web/src/main/js/types/issues.ts
  28. 3
    0
      server/sonar-web/src/main/js/types/types.ts

+ 190
- 11
server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts View File

@@ -17,13 +17,29 @@
* 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 {
@@ -34,16 +50,123 @@ function mockReferenceComponent(override?: Partial<ReferencedComponent>) {
};
}

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> {
@@ -65,7 +188,63 @@ export default class IssuesServiceMock {
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();
@@ -79,7 +258,7 @@ export default class IssuesServiceMock {
components: [mockReferenceComponent()],
effortTotal: 199629,
facets,
issues: [],
issues: this.list.map(line => line.issue),
languages: [],
paging: mockPaging()
});

+ 1
- 1
server/sonar-web/src/main/js/api/mocks/SourceViewerServiceMock.ts View File

@@ -26,7 +26,7 @@ import {
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';


+ 4
- 4
server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetails-test.tsx.snap View File

@@ -31,7 +31,7 @@ exports[`should render correctly: loaded 1`] = `
"defaultRemFnType": "CONSTANT_ISSUE",
"descriptionSections": Array [
Object {
"content": "<b>Why<b/> Because",
"content": "<b>Why</b> Because",
"key": "root_cause",
},
],
@@ -73,7 +73,7 @@ exports[`should render correctly: loaded 1`] = `
"defaultRemFnType": "CONSTANT_ISSUE",
"descriptionSections": Array [
Object {
"content": "<b>Why<b/> Because",
"content": "<b>Why</b> Because",
"key": "root_cause",
},
],
@@ -146,7 +146,7 @@ exports[`should render correctly: loaded 1`] = `
"defaultRemFnType": "CONSTANT_ISSUE",
"descriptionSections": Array [
Object {
"content": "<b>Why<b/> Because",
"content": "<b>Why</b> Because",
"key": "root_cause",
},
],
@@ -187,7 +187,7 @@ exports[`should render correctly: loaded 1`] = `
"defaultRemFnType": "CONSTANT_ISSUE",
"descriptionSections": Array [
Object {
"content": "<b>Why<b/> Because",
"content": "<b>Why</b> Because",
"key": "root_cause",
},
],

+ 47
- 2
server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx View File

@@ -25,14 +25,59 @@ import { renderComponentApp } from '../../../helpers/testReactTestingUtils';
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();

+ 3
- 2
server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewerWrapper.tsx View File

@@ -38,6 +38,7 @@ import { WorkspaceContext } from '../../../components/workspace/context';
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 {
@@ -151,11 +152,11 @@ export default class CrossComponentSourceViewerWrapper extends React.PureCompone
}
} 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 });
}
}
}

+ 23
- 20
server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/ComponentSourceSnippetGroupViewer-test.tsx View File

@@ -24,12 +24,11 @@ import { getSources } from '../../../../api/components';
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';
@@ -50,6 +49,7 @@ it('should render correctly', () => {
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 }
});
@@ -65,7 +65,7 @@ it('should render correctly with secondary locations', () => {
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)
@@ -81,6 +81,7 @@ it('should render correctly with secondary locations', () => {
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 }
});
@@ -96,7 +97,7 @@ it('should render correctly with flows', () => {
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)
@@ -129,6 +130,7 @@ it('should render correctly with flows', () => {
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
});
@@ -142,7 +144,7 @@ it('should render file-level issue correctly', () => {
textRange: { startLine: 34, endLine: 34, startOffset: 0, endOffset: 0 }
})
],
...mockSnippetsByComponent(issue.component, range(29, 39))
...mockSnippetsByComponent('main.js', 'project', range(29, 39))
}
});

@@ -151,7 +153,7 @@ it('should render file-level issue correctly', () => {

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 }
@@ -167,7 +169,7 @@ it('should expand block', async () => {
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 });
@@ -175,7 +177,7 @@ it('should expand block', async () => {
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);
@@ -183,7 +185,7 @@ it('should expand block', async () => {

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: [
@@ -196,7 +198,7 @@ it('should expand full component', async () => {
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 });
@@ -204,7 +206,7 @@ it('should expand full component', async () => {
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 });
});
@@ -212,12 +214,13 @@ it('should expand full component', async () => {
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({
@@ -228,7 +231,7 @@ it('should get the right branch when expanding', async () => {
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', () => {
@@ -254,15 +257,15 @@ it('should correctly handle lines actions', () => {
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();
@@ -278,14 +281,14 @@ it('should correctly handle lines actions', () => {
.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
);

+ 3
- 4
server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/CrossComponentSourceViewerWrapper-test.tsx View File

@@ -22,17 +22,16 @@ import * as React from 'react';
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() })
};

+ 3
- 2
server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/SnippetViewer-test.tsx View File

@@ -20,8 +20,9 @@
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', () => ({
@@ -82,7 +83,7 @@ it('should render correctly when at the top of the file', () => {
});

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,

+ 6
- 2
server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/ComponentSourceSnippetGroupViewer-test.tsx.snap View File

@@ -18,15 +18,19 @@ exports[`should render correctly 1`] = `
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",

+ 25
- 9
server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/CrossComponentSourceViewerWrapper-test.tsx.snap View File

@@ -29,15 +29,19 @@ exports[`should render correctly 2`] = `
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",
@@ -47,7 +51,7 @@ exports[`should render correctly 2`] = `
>
<ComponentSourceSnippetGroupViewer
duplicationsByLine={Object {}}
isLastOccurenceOfPrimaryComponent={true}
isLastOccurenceOfPrimaryComponent={false}
issue={
Object {
"actions": Array [],
@@ -173,15 +177,19 @@ exports[`should render correctly 2`] = `
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",
@@ -356,15 +364,19 @@ exports[`should render correctly: no component found 1`] = `
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",
@@ -500,15 +512,19 @@ exports[`should render correctly: no component found 1`] = `
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",

+ 21
- 11
server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/utils-test.ts View File

@@ -17,11 +17,8 @@
* 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', () => {
@@ -43,7 +40,20 @@ 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);
@@ -67,15 +77,15 @@ describe('groupLocationsByComponent', () => {
})
],
{
'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);

+ 2
- 1
server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainer-test.tsx View File

@@ -24,7 +24,8 @@ import { getSources } from '../../../../api/components';
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';

+ 1
- 1
server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainerRenderer-test.tsx View File

@@ -22,8 +22,8 @@ import React, { RefObject } from 'react';
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,

+ 6
- 2
server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSnippetContainerRenderer-test.tsx.snap View File

@@ -402,15 +402,19 @@ exports[`should render correctly: with sourcelines 1`] = `
<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",

+ 2
- 1
server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewerBase-test.tsx View File

@@ -21,7 +21,8 @@ import { shallow } from 'enzyme';
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';

+ 2
- 1
server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewerCode-test.tsx View File

@@ -20,7 +20,8 @@
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';

+ 2
- 2
server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewerHeader-test.tsx View File

@@ -20,7 +20,7 @@
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';
@@ -34,7 +34,7 @@ it('should render correctly for a unit test', () => {
expect(
shallowRender({
showMeasures: true,
sourceViewerFile: mockSourceViewerFile({
sourceViewerFile: mockSourceViewerFile('foo/bar.ts', 'my-project', {
q: ComponentQualifier.TestFile,
measures: { tests: '12' }
})

+ 11
- 4
server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewerHeaderSlim-test.tsx View File

@@ -20,7 +20,7 @@
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';

@@ -29,14 +29,21 @@ it('should render correctly', () => {
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();
});
@@ -47,7 +54,7 @@ function shallowRender(props: Partial<Props> = {}) {
branchLike={mockMainBranch()}
expandable={true}
onExpand={jest.fn()}
sourceViewerFile={mockSourceViewerFile()}
sourceViewerFile={mockSourceViewerFile('foo/bar.ts', 'my-project')}
{...props}
/>
);

+ 6
- 2
server/sonar-web/src/main/js/components/SourceViewer/__tests__/__snapshots__/SourceViewerBase-test.tsx.snap View File

@@ -11,16 +11,20 @@ exports[`should render correctly 1`] = `
"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",

+ 14
- 14
server/sonar-web/src/main/js/components/SourceViewer/__tests__/__snapshots__/SourceViewerHeader-test.tsx.snap View File

@@ -15,7 +15,7 @@ exports[`should render correctly for a regular file 1`] = `
>
<a
className="link-with-icon"
href="/dashboard?id=my-project"
href="/dashboard?id=project"
>
<QualifierIcon
qualifier="TRK"
@@ -79,9 +79,9 @@ exports[`should render correctly for a regular file 1`] = `
Object {
"pathname": "/code",
"query": Object {
"id": "my-project",
"id": "project",
"line": undefined,
"selected": "foo",
"selected": "project:foo/bar.ts",
},
}
}
@@ -101,7 +101,7 @@ exports[`should render correctly for a regular file 1`] = `
<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"
>
@@ -220,7 +220,7 @@ exports[`should render correctly for a unit test 1`] = `
"query": Object {
"id": "my-project",
"line": undefined,
"selected": "foo",
"selected": "my-project:foo/bar.ts",
},
}
}
@@ -240,7 +240,7 @@ exports[`should render correctly for a unit test 1`] = `
<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"
>
@@ -275,7 +275,7 @@ exports[`should render correctly if issue details are passed 1`] = `
>
<a
className="link-with-icon"
href="/dashboard?id=my-project"
href="/dashboard?id=project"
>
<QualifierIcon
qualifier="TRK"
@@ -381,7 +381,7 @@ exports[`should render correctly if issue details are passed 1`] = `
"pathname": "/project/issues",
"query": Object {
"files": "foo/bar.ts",
"id": "my-project",
"id": "project",
"resolved": "false",
"types": "BUG",
},
@@ -412,7 +412,7 @@ exports[`should render correctly if issue details are passed 1`] = `
"pathname": "/project/issues",
"query": Object {
"files": "foo/bar.ts",
"id": "my-project",
"id": "project",
"resolved": "false",
"types": "VULNERABILITY",
},
@@ -443,7 +443,7 @@ exports[`should render correctly if issue details are passed 1`] = `
"pathname": "/project/issues",
"query": Object {
"files": "foo/bar.ts",
"id": "my-project",
"id": "project",
"resolved": "false",
"types": "CODE_SMELL",
},
@@ -474,7 +474,7 @@ exports[`should render correctly if issue details are passed 1`] = `
"pathname": "/project/issues",
"query": Object {
"files": "foo/bar.ts",
"id": "my-project",
"id": "project",
"resolved": "false",
"types": "SECURITY_HOTSPOT",
},
@@ -512,9 +512,9 @@ exports[`should render correctly if issue details are passed 1`] = `
Object {
"pathname": "/code",
"query": Object {
"id": "my-project",
"id": "project",
"line": undefined,
"selected": "foo",
"selected": "project:foo/bar.ts",
},
}
}
@@ -534,7 +534,7 @@ exports[`should render correctly if issue details are passed 1`] = `
<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"
>

+ 2
- 1
server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/Line-test.tsx View File

@@ -19,7 +19,8 @@
*/
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', () => {

+ 1
- 1
server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineCode-test.tsx View File

@@ -19,7 +19,7 @@
*/
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', () => {

+ 2
- 1
server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineIssueList-test.tsx View File

@@ -20,7 +20,8 @@
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', () => {

+ 2
- 1
server/sonar-web/src/main/js/components/SourceViewer/helpers/__tests__/issueLocations-test.ts View File

@@ -17,7 +17,8 @@
* 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', () => {

+ 78
- 0
server/sonar-web/src/main/js/helpers/mocks/sources.ts View File

@@ -0,0 +1,78 @@
/*
* 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
};
}

+ 12
- 57
server/sonar-web/src/main/js/helpers/testMocks.ts View File

@@ -23,9 +23,8 @@ import { DocumentationEntry } from '../apps/documentation/utils';
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';
@@ -51,9 +50,6 @@ import {
RuleDescriptionSections,
RuleDetails,
RuleParameter,
SnippetsByComponent,
SourceLine,
SourceViewerFile,
SysInfoBase,
SysInfoCluster,
SysInfoStandalone
@@ -301,38 +297,6 @@ export function mockCondition(overrides: Partial<Condition> = {}): Condition {
};
}

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,
@@ -372,6 +336,7 @@ export function mockEvent(overrides = {}) {

export function mockRawIssue(withLocations = false, overrides: Partial<RawIssue> = {}): RawIssue {
const rawIssue: RawIssue = {
actions: [],
component: 'main.js',
key: 'AVsae-CQS-9G3txfbFN2',
line: 25,
@@ -380,13 +345,21 @@ export function mockRawIssue(withLocations = false, overrides: Partial<RawIssue>
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 {
@@ -608,7 +581,7 @@ export function mockRuleDetails(overrides: Partial<RuleDetails> = {}): RuleDetai
descriptionSections: [
{
key: RuleDescriptionSections.ROOT_CAUSE,
content: '<b>Why<b/> Because'
content: '<b>Why</b> Because'
}
],
htmlDesc: '',
@@ -648,24 +621,6 @@ export function mockRuleDetailsParameter(overrides: Partial<RuleParameter> = {})
};
}

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 {

+ 3
- 0
server/sonar-web/src/main/js/types/issues.ts View File

@@ -42,6 +42,7 @@ interface Comment {
}

export interface RawIssue {
actions: string[];
assignee?: string;
author?: string;
comments?: Array<Comment>;
@@ -54,10 +55,12 @@ export interface RawIssue {
line?: number;
project: string;
rule: string;
message?: string;
severity: string;
status: string;
subProject?: string;
textRange?: TextRange;
type: IssueType;
}

export interface IssueResponse {

+ 3
- 0
server/sonar-web/src/main/js/types/types.ts View File

@@ -674,7 +674,10 @@ export interface SourceViewerFile {
lines?: string;
tests?: string;
};
canMarkAsFavorite?: boolean;
path: string;
name?: string;
longName?: string;
project: string;
projectName: string;
q: ComponentQualifier;

Loading…
Cancel
Save