@@ -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() | |||
}); |
@@ -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'; | |||
@@ -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", | |||
}, | |||
], |
@@ -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(); |
@@ -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 }); | |||
} | |||
} | |||
} |
@@ -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 | |||
); |
@@ -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() }) | |||
}; |
@@ -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, |
@@ -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", |
@@ -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", |
@@ -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); |
@@ -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'; |
@@ -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, |
@@ -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", |
@@ -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'; |
@@ -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'; |
@@ -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' } | |||
}) |
@@ -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} | |||
/> | |||
); |
@@ -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", |
@@ -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" | |||
> |
@@ -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', () => { |
@@ -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', () => { |
@@ -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', () => { |
@@ -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', () => { |
@@ -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 | |||
}; | |||
} |
@@ -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 { |
@@ -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 { |
@@ -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; |