Browse Source

SONAR-16008 Move hotspot code to a tab

tags/9.4.0.54424
Jeremy Davis 2 years ago
parent
commit
1a4de4eb41

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

@@ -79,7 +79,7 @@ export default function HotspotSnippetContainerRenderer(
<p className="spacer-bottom">{translate('hotspots.no_associated_lines')}</p>
)}
<HotspotSnippetHeader hotspot={hotspot} component={component} branchLike={branchLike} />
<div className="bordered big-spacer-bottom">
<div className="bordered">
<DeferredSpinner className="big-spacer" loading={loading}>
{sourceLines.length > 0 && (
<SnippetViewer

+ 9
- 5
server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerRenderer.tsx View File

@@ -71,13 +71,17 @@ export function HotspotViewerRenderer(props: HotspotViewerRendererProps) {
{hotspot && (
<div className="big-padded hotspot-content">
<HotspotHeader hotspot={hotspot} onUpdateHotspot={props.onUpdateHotspot} />
<HotspotSnippetContainer
branchLike={fillBranchLike(hotspot.project.branch, hotspot.project.pullRequest)}
component={component}
<HotspotViewerTabs
codeTabContent={
<HotspotSnippetContainer
branchLike={fillBranchLike(hotspot.project.branch, hotspot.project.pullRequest)}
component={component}
hotspot={hotspot}
onCommentButtonClick={props.onShowCommentForm}
/>
}
hotspot={hotspot}
onCommentButtonClick={props.onShowCommentForm}
/>
<HotspotViewerTabs hotspot={hotspot} />
<HotspotReviewHistoryAndComments
commentTextRef={commentTextRef}
currentUser={currentUser}

+ 24
- 11
server/sonar-web/src/main/js/apps/security-hotspots/components/HotspotViewerTabs.tsx View File

@@ -19,12 +19,12 @@
*/
import * as React from 'react';
import BoxedTabs from '../../../components/controls/BoxedTabs';
import Tab from '../../../components/controls/Tabs';
import { translate } from '../../../helpers/l10n';
import { sanitizeString } from '../../../helpers/sanitize';
import { Hotspot } from '../../../types/security-hotspots';

interface Props {
codeTabContent: React.ReactNode;
hotspot: Hotspot;
}

@@ -40,6 +40,7 @@ interface Tab {
}

export enum TabKeys {
Code = 'code',
RiskDescription = 'risk',
VulnerabilityDescription = 'vulnerability',
FixRecommendation = 'fix'
@@ -73,7 +74,8 @@ export default class HotspotViewerTabs extends React.PureComponent<Props, State>

computeTabs() {
const { hotspot } = this.props;
return [

const descriptionTabs = [
{
key: TabKeys.RiskDescription,
label: translate('hotspots.tabs.risk_description'),
@@ -89,24 +91,35 @@ export default class HotspotViewerTabs extends React.PureComponent<Props, State>
label: translate('hotspots.tabs.fix_recommendations'),
content: hotspot.rule.fixRecommendations || ''
}
].filter(tab => Boolean(tab.content));
].filter(tab => tab.content.length > 0);

return [
{
key: TabKeys.Code,
label: translate('hotspots.tabs.code'),
content: ''
},
...descriptionTabs
];
}

render() {
const { codeTabContent } = this.props;
const { tabs, currentTab } = this.state;
if (tabs.length === 0) {
return null;
}

return (
<>
<BoxedTabs onSelect={this.handleSelectTabs} selected={currentTab.key} tabs={tabs} />
<div className="bordered huge-spacer-bottom">
<div
className="markdown big-padded"
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: sanitizeString(currentTab.content) }}
/>
{currentTab.key === TabKeys.Code ? (
<div className="padded">{codeTabContent}</div>
) : (
<div
className="markdown big-padded"
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{ __html: sanitizeString(currentTab.content) }}
/>
)}
</div>
</>
);

+ 15
- 7
server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/HotspotViewerTabs-test.tsx View File

@@ -19,7 +19,7 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
import BoxedTabs from '../../../../components/controls/BoxedTabs';
import BoxedTabs, { BoxedTabsProps } from '../../../../components/controls/BoxedTabs';
import { mockHotspot, mockHotspotRule } from '../../../../helpers/mocks/security-hotspots';
import { mockUser } from '../../../../helpers/testMocks';
import HotspotViewerTabs, { TabKeys } from '../HotspotViewerTabs';
@@ -28,7 +28,7 @@ it('should render correctly', () => {
const wrapper = shallowRender();
expect(wrapper).toMatchSnapshot('risk');

const onSelect = wrapper.find(BoxedTabs).prop('onSelect') as (tab: TabKeys) => void;
const onSelect: (tab: TabKeys) => void = wrapper.find(BoxedTabs).prop('onSelect');

onSelect(TabKeys.VulnerabilityDescription);
expect(wrapper).toMatchSnapshot('vulnerability');
@@ -46,8 +46,10 @@ it('should render correctly', () => {
vulnerabilityDescription: undefined
})
})
}).type()
).toBeNull();
})
.find<BoxedTabsProps<string>>(BoxedTabs)
.props().tabs
).toHaveLength(1);

expect(
shallowRender({
@@ -86,14 +88,20 @@ it('should filter empty tab', () => {

it('should select first tab on hotspot update', () => {
const wrapper = shallowRender();
const onSelect = wrapper.find(BoxedTabs).prop('onSelect') as (tab: TabKeys) => void;
const onSelect: (tab: TabKeys) => void = wrapper.find(BoxedTabs).prop('onSelect');

onSelect(TabKeys.VulnerabilityDescription);
expect(wrapper.state().currentTab.key).toBe(TabKeys.VulnerabilityDescription);
wrapper.setProps({ hotspot: mockHotspot({ key: 'new_key' }) });
expect(wrapper.state().currentTab.key).toBe(TabKeys.RiskDescription);
expect(wrapper.state().currentTab.key).toBe(TabKeys.Code);
});

function shallowRender(props?: Partial<HotspotViewerTabs['props']>) {
return shallow<HotspotViewerTabs>(<HotspotViewerTabs hotspot={mockHotspot()} {...props} />);
return shallow<HotspotViewerTabs>(
<HotspotViewerTabs
codeTabContent={<div>CodeTabContent</div>}
hotspot={mockHotspot()}
{...props}
/>
);
}

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

@@ -126,7 +126,7 @@ exports[`should render correctly 1`] = `
}
/>
<div
className="bordered big-spacer-bottom"
className="bordered"
>
<DeferredSpinner
className="big-spacer"
@@ -257,7 +257,7 @@ exports[`should render correctly: with sourcelines 1`] = `
}
/>
<div
className="bordered big-spacer-bottom"
className="bordered"
>
<DeferredSpinner
className="big-spacer"

+ 783
- 771
server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotViewerRenderer-test.tsx.snap
File diff suppressed because it is too large
View File


+ 34
- 16
server/sonar-web/src/main/js/apps/security-hotspots/components/__tests__/__snapshots__/HotspotViewerTabs-test.tsx.snap View File

@@ -7,6 +7,11 @@ exports[`should render correctly: fix 1`] = `
selected="fix"
tabs={
Array [
Object {
"content": "",
"key": "code",
"label": "hotspots.tabs.code",
},
Object {
"content": "<p>This a <strong>strong</strong> message about risk !</p>",
"key": "risk",
@@ -44,9 +49,14 @@ exports[`should render correctly: risk 1`] = `
<Fragment>
<BoxedTabs
onSelect={[Function]}
selected="risk"
selected="code"
tabs={
Array [
Object {
"content": "",
"key": "code",
"label": "hotspots.tabs.code",
},
Object {
"content": "<p>This a <strong>strong</strong> message about risk !</p>",
"key": "risk",
@@ -69,13 +79,12 @@ exports[`should render correctly: risk 1`] = `
className="bordered huge-spacer-bottom"
>
<div
className="markdown big-padded"
dangerouslySetInnerHTML={
Object {
"__html": "<p>This a <strong>strong</strong> message about risk !</p>",
}
}
/>
className="padded"
>
<div>
CodeTabContent
</div>
</div>
</div>
</Fragment>
`;
@@ -87,6 +96,11 @@ exports[`should render correctly: vulnerability 1`] = `
selected="vulnerability"
tabs={
Array [
Object {
"content": "",
"key": "code",
"label": "hotspots.tabs.code",
},
Object {
"content": "<p>This a <strong>strong</strong> message about risk !</p>",
"key": "risk",
@@ -124,9 +138,14 @@ exports[`should render correctly: with comments or changelog element 1`] = `
<Fragment>
<BoxedTabs
onSelect={[Function]}
selected="risk"
selected="code"
tabs={
Array [
Object {
"content": "",
"key": "code",
"label": "hotspots.tabs.code",
},
Object {
"content": "<p>This a <strong>strong</strong> message about risk !</p>",
"key": "risk",
@@ -149,13 +168,12 @@ exports[`should render correctly: with comments or changelog element 1`] = `
className="bordered huge-spacer-bottom"
>
<div
className="markdown big-padded"
dangerouslySetInnerHTML={
Object {
"__html": "<p>This a <strong>strong</strong> message about risk !</p>",
}
}
/>
className="padded"
>
<div>
CodeTabContent
</div>
</div>
</div>
</Fragment>
`;

+ 2
- 1
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

@@ -735,8 +735,9 @@ hotspots.list_title.FIXED={0} Security Hotspots reviewed as fixed
hotspots.list_title.SAFE={0} Security Hotspots reviewed as safe
hotspots.risk_exposure=Review priority

hotspots.tabs.code=Where is the risk?
hotspots.tabs.risk_description=What's the risk?
hotspots.tabs.vulnerability_description=Are you at risk?
hotspots.tabs.vulnerability_description=Assess the risk
hotspots.tabs.fix_recommendations=How can you fix it?
hotspots.review_history.created=created Security Hotspot
hotspots.review_history.comment_added=added a comment

Loading…
Cancel
Save