From aaf931d953e0610e875b24fd21069157330b8965 Mon Sep 17 00:00:00 2001 From: Mathieu Suen Date: Tue, 24 May 2022 14:08:29 +0200 Subject: [PATCH] SONAR-16303 Add frontend IT for advance rule --- .../main/js/api/mocks/IssuesServiceMock.ts | 201 +++++++++++++++++- .../js/api/mocks/SourceViewerServiceMock.ts | 2 +- .../__snapshots__/RuleDetails-test.tsx.snap | 8 +- .../js/apps/issues/__tests__/IssueApp-it.tsx | 49 ++++- .../CrossComponentSourceViewerWrapper.tsx | 5 +- ...ComponentSourceSnippetGroupViewer-test.tsx | 43 ++-- ...CrossComponentSourceViewerWrapper-test.tsx | 7 +- .../__tests__/SnippetViewer-test.tsx | 5 +- ...nentSourceSnippetGroupViewer-test.tsx.snap | 8 +- ...ComponentSourceViewerWrapper-test.tsx.snap | 34 ++- .../__tests__/utils-test.ts | 32 ++- .../HotspotSnippetContainer-test.tsx | 3 +- .../HotspotSnippetContainerRenderer-test.tsx | 2 +- ...spotSnippetContainerRenderer-test.tsx.snap | 8 +- .../__tests__/SourceViewerBase-test.tsx | 3 +- .../__tests__/SourceViewerCode-test.tsx | 3 +- .../__tests__/SourceViewerHeader-test.tsx | 4 +- .../__tests__/SourceViewerHeaderSlim-test.tsx | 15 +- .../SourceViewerBase-test.tsx.snap | 8 +- .../SourceViewerHeader-test.tsx.snap | 28 +-- .../components/__tests__/Line-test.tsx | 3 +- .../components/__tests__/LineCode-test.tsx | 2 +- .../__tests__/LineIssueList-test.tsx | 3 +- .../helpers/__tests__/issueLocations-test.ts | 3 +- .../src/main/js/helpers/mocks/sources.ts | 78 +++++++ .../src/main/js/helpers/testMocks.ts | 69 ++---- server/sonar-web/src/main/js/types/issues.ts | 3 + server/sonar-web/src/main/js/types/types.ts | 3 + 28 files changed, 475 insertions(+), 157 deletions(-) create mode 100644 server/sonar-web/src/main/js/helpers/mocks/sources.ts diff --git a/server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts index 3d1ed2a6a22..e961edf5b66 100644 --- a/server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts +++ b/server/sonar-web/src/main/js/api/mocks/IssuesServiceMock.ts @@ -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) { return { @@ -34,16 +50,123 @@ function mockReferenceComponent(override?: Partial) { }; } +interface IssueData { + issue: RawIssue; + snippets: Dict; +} + 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 { @@ -65,7 +188,63 @@ export default class IssuesServiceMock { this.isAdmin = isAdmin; } - listHandler = (query: RequestData): Promise => { + 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> => { + 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: '

Into

' }, + { key: RuleDescriptionSections.ROOT_CAUSE, content: '

Because

' }, + { key: RuleDescriptionSections.HOW_TO_FIX, content: '

Fix with

' }, + { key: RuleDescriptionSections.RESOURCES, content: '

Link

' } + ] + }) + }); + } + return this.reply({ + rule: mockRuleDetails({ + key: parameters.key, + name: 'Simple rule', + htmlNote: '

Note

', + descriptionSections: [ + { + key: RuleDescriptionSections.DEFAULT, + content: '

Default

Default description' + } + ] + }) + }); + }; + + handleSearchIssues = (query: RequestData): Promise => { 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() }); diff --git a/server/sonar-web/src/main/js/api/mocks/SourceViewerServiceMock.ts b/server/sonar-web/src/main/js/api/mocks/SourceViewerServiceMock.ts index 636c304c0d6..7179d0f26e0 100644 --- a/server/sonar-web/src/main/js/api/mocks/SourceViewerServiceMock.ts +++ b/server/sonar-web/src/main/js/api/mocks/SourceViewerServiceMock.ts @@ -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'; diff --git a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetails-test.tsx.snap b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetails-test.tsx.snap index d00bb73d453..a5cdd29cc53 100644 --- a/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetails-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/coding-rules/components/__tests__/__snapshots__/RuleDetails-test.tsx.snap @@ -31,7 +31,7 @@ exports[`should render correctly: loaded 1`] = ` "defaultRemFnType": "CONSTANT_ISSUE", "descriptionSections": Array [ Object { - "content": "Why Because", + "content": "Why Because", "key": "root_cause", }, ], @@ -73,7 +73,7 @@ exports[`should render correctly: loaded 1`] = ` "defaultRemFnType": "CONSTANT_ISSUE", "descriptionSections": Array [ Object { - "content": "Why Because", + "content": "Why Because", "key": "root_cause", }, ], @@ -146,7 +146,7 @@ exports[`should render correctly: loaded 1`] = ` "defaultRemFnType": "CONSTANT_ISSUE", "descriptionSections": Array [ Object { - "content": "Why Because", + "content": "Why Because", "key": "root_cause", }, ], @@ -187,7 +187,7 @@ exports[`should render correctly: loaded 1`] = ` "defaultRemFnType": "CONSTANT_ISSUE", "descriptionSections": Array [ Object { - "content": "Why Because", + "content": "Why Because", "key": "root_cause", }, ], diff --git a/server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx b/server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx index 0a99fb39b8f..8153bf39579 100644 --- a/server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx +++ b/server/sonar-web/src/main/js/apps/issues/__tests__/IssueApp-it.tsx @@ -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(); diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewerWrapper.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewerWrapper.tsx index 676c7701ec6..2d20f25c5ac 100644 --- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewerWrapper.tsx +++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/CrossComponentSourceViewerWrapper.tsx @@ -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 }); } } } diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/ComponentSourceSnippetGroupViewer-test.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/ComponentSourceSnippetGroupViewer-test.tsx index 5db19b60cc0..cbbe8669da5 100644 --- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/ComponentSourceSnippetGroupViewer-test.tsx +++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/ComponentSourceSnippetGroupViewer-test.tsx @@ -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('loadDuplications')(line); - expect(loadDuplications).toHaveBeenCalledWith('a', line); + expect(loadDuplications).toHaveBeenCalledWith('my-project:foo/bar.ts', line); wrapper .find('SnippetViewer') .first() .prop('renderDuplicationPopup')(1, 13); expect(renderDuplicationPopup).toHaveBeenCalledWith( - mockSourceViewerFile({ key: 'a', path: 'a' }), + mockSourceViewerFile('foo/bar.ts', 'my-project'), 1, 13 ); diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/CrossComponentSourceViewerWrapper-test.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/CrossComponentSourceViewerWrapper-test.tsx index bc2e2802079..050f69d8df6 100644 --- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/CrossComponentSourceViewerWrapper-test.tsx +++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/CrossComponentSourceViewerWrapper-test.tsx @@ -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() }) }; diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/SnippetViewer-test.tsx b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/SnippetViewer-test.tsx index 76fb05d7c18..579ca3c257b 100644 --- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/SnippetViewer-test.tsx +++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/SnippetViewer-test.tsx @@ -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, diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/ComponentSourceSnippetGroupViewer-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/ComponentSourceSnippetGroupViewer-test.tsx.snap index c98ca92f56f..c19827b9ef7 100644 --- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/ComponentSourceSnippetGroupViewer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/ComponentSourceSnippetGroupViewer-test.tsx.snap @@ -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", diff --git a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/CrossComponentSourceViewerWrapper-test.tsx.snap b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/CrossComponentSourceViewerWrapper-test.tsx.snap index 1c7210b0168..7027b2e58a7 100644 --- a/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/CrossComponentSourceViewerWrapper-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/issues/crossComponentSourceViewer/__tests__/__snapshots__/CrossComponentSourceViewerWrapper-test.tsx.snap @@ -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`] = ` > { @@ -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); diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainer-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainer-test.tsx index 53ea02c8505..d984d488ec4 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainer-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainer-test.tsx @@ -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'; diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainerRenderer-test.tsx b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainerRenderer-test.tsx index 1e58285c233..98aec410c5c 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainerRenderer-test.tsx +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotSnippetContainerRenderer-test.tsx @@ -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, diff --git a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSnippetContainerRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSnippetContainerRenderer-test.tsx.snap index be77b3e2008..a4ade78ebae 100644 --- a/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSnippetContainerRenderer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotSnippetContainerRenderer-test.tsx.snap @@ -402,15 +402,19 @@ exports[`should render correctly: with sourcelines 1`] = ` { expect( shallowRender({ showMeasures: true, - sourceViewerFile: mockSourceViewerFile({ + sourceViewerFile: mockSourceViewerFile('foo/bar.ts', 'my-project', { q: ComponentQualifier.TestFile, measures: { tests: '12' } }) diff --git a/server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewerHeaderSlim-test.tsx b/server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewerHeaderSlim-test.tsx index 82232a309b3..86113ca61a2 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewerHeaderSlim-test.tsx +++ b/server/sonar-web/src/main/js/components/SourceViewer/__tests__/SourceViewerHeaderSlim-test.tsx @@ -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 = {}) { branchLike={mockMainBranch()} expandable={true} onExpand={jest.fn()} - sourceViewerFile={mockSourceViewerFile()} + sourceViewerFile={mockSourceViewerFile('foo/bar.ts', 'my-project')} {...props} /> ); diff --git a/server/sonar-web/src/main/js/components/SourceViewer/__tests__/__snapshots__/SourceViewerBase-test.tsx.snap b/server/sonar-web/src/main/js/components/SourceViewer/__tests__/__snapshots__/SourceViewerBase-test.tsx.snap index 62e0125c3de..ef8cdb4d1ba 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/__tests__/__snapshots__/SourceViewerBase-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/SourceViewer/__tests__/__snapshots__/SourceViewerBase-test.tsx.snap @@ -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", diff --git a/server/sonar-web/src/main/js/components/SourceViewer/__tests__/__snapshots__/SourceViewerHeader-test.tsx.snap b/server/sonar-web/src/main/js/components/SourceViewer/__tests__/__snapshots__/SourceViewerHeader-test.tsx.snap index eaf213f5dcf..58ecb2f6e0b 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/__tests__/__snapshots__/SourceViewerHeader-test.tsx.snap +++ b/server/sonar-web/src/main/js/components/SourceViewer/__tests__/__snapshots__/SourceViewerHeader-test.tsx.snap @@ -15,7 +15,7 @@ exports[`should render correctly for a regular file 1`] = ` > @@ -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`] = `
  • @@ -275,7 +275,7 @@ exports[`should render correctly if issue details are passed 1`] = ` > diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/Line-test.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/Line-test.tsx index cb3b97075cb..82add319eba 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/Line-test.tsx +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/Line-test.tsx @@ -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', () => { diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineCode-test.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineCode-test.tsx index 8d15db4f708..5339dfc9828 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineCode-test.tsx +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineCode-test.tsx @@ -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', () => { diff --git a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineIssueList-test.tsx b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineIssueList-test.tsx index 8d803bbb172..712221baa93 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineIssueList-test.tsx +++ b/server/sonar-web/src/main/js/components/SourceViewer/components/__tests__/LineIssueList-test.tsx @@ -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', () => { diff --git a/server/sonar-web/src/main/js/components/SourceViewer/helpers/__tests__/issueLocations-test.ts b/server/sonar-web/src/main/js/components/SourceViewer/helpers/__tests__/issueLocations-test.ts index 5c1f9884ec5..265c7d58e14 100644 --- a/server/sonar-web/src/main/js/components/SourceViewer/helpers/__tests__/issueLocations-test.ts +++ b/server/sonar-web/src/main/js/components/SourceViewer/helpers/__tests__/issueLocations-test.ts @@ -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', () => { diff --git a/server/sonar-web/src/main/js/helpers/mocks/sources.ts b/server/sonar-web/src/main/js/helpers/mocks/sources.ts new file mode 100644 index 00000000000..72523c2add2 --- /dev/null +++ b/server/sonar-web/src/main/js/helpers/mocks/sources.ts @@ -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 { + 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 { + return { + line: 16, + code: 'import java.util.ArrayList;', + 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 + }; +} diff --git a/server/sonar-web/src/main/js/helpers/testMocks.ts b/server/sonar-web/src/main/js/helpers/testMocks.ts index dd4270c4c43..80bab8e94d6 100644 --- a/server/sonar-web/src/main/js/helpers/testMocks.ts +++ b/server/sonar-web/src/main/js/helpers/testMocks.ts @@ -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 { }; } -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 { - return { - line: 16, - code: 'import java.util.ArrayList;', - 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 { return { isLoggedIn: false, @@ -372,6 +336,7 @@ export function mockEvent(overrides = {}) { export function mockRawIssue(withLocations = false, overrides: Partial = {}): 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 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 = {}): RuleDetai descriptionSections: [ { key: RuleDescriptionSections.ROOT_CAUSE, - content: 'Why Because' + content: 'Why Because' } ], htmlDesc: '', @@ -648,24 +621,6 @@ export function mockRuleDetailsParameter(overrides: Partial = {}) }; } -export function mockSourceViewerFile(overrides: Partial = {}): 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 = {}): SysInfoStandalone { const baseInfo = mockBaseSysInfo(overrides); return { diff --git a/server/sonar-web/src/main/js/types/issues.ts b/server/sonar-web/src/main/js/types/issues.ts index 18f6e322f19..360e4e15312 100644 --- a/server/sonar-web/src/main/js/types/issues.ts +++ b/server/sonar-web/src/main/js/types/issues.ts @@ -42,6 +42,7 @@ interface Comment { } export interface RawIssue { + actions: string[]; assignee?: string; author?: string; comments?: Array; @@ -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 { diff --git a/server/sonar-web/src/main/js/types/types.ts b/server/sonar-web/src/main/js/types/types.ts index 6d857540864..5691a852d8f 100644 --- a/server/sonar-web/src/main/js/types/types.ts +++ b/server/sonar-web/src/main/js/types/types.ts @@ -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; -- 2.39.5