Browse Source

SONAR-12720 comment status changes

tags/8.2.0.32929
Jeremy 4 years ago
parent
commit
692623ac5c

+ 4
- 0
server/sonar-web/src/main/js/app/styles/init/forms.css View File

@@ -138,6 +138,10 @@ textarea.width-100 {
max-width: 100%;
}

textarea.fixed-width {
resize: vertical;
}

select {
height: var(--controlHeight);
line-height: var(--controlHeight);

+ 20
- 6
server/sonar-web/src/main/js/apps/securityHotspots/components/HotspotActionsForm.tsx View File

@@ -34,6 +34,7 @@ interface Props {
}

interface State {
comment: string;
selectedUser?: T.UserActive;
selectedOption: HotspotStatusOptions;
submitting: boolean;
@@ -41,6 +42,7 @@ interface State {

export default class HotspotActionsForm extends React.Component<Props, State> {
state: State = {
comment: '',
selectedOption: HotspotStatusOptions.FIXED,
submitting: false
};
@@ -53,17 +55,27 @@ export default class HotspotActionsForm extends React.Component<Props, State> {
this.setState({ selectedUser });
};

handleCommentChange = (comment: string) => {
this.setState({ comment });
};

handleSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
event.preventDefault();

const { hotspotKey } = this.props;
const { selectedOption } = this.state;
const { comment, selectedOption, selectedUser } = this.state;

const status =
selectedOption === HotspotStatusOptions.ADDITIONAL_REVIEW
? HotspotStatus.TO_REVIEW
: HotspotStatus.REVIEWED;
const data: HotspotSetStatusRequest = { status };

// If reassigning, ignore comment for status update. It will be sent with the reassignment below
if (comment && !(selectedOption === HotspotStatusOptions.ADDITIONAL_REVIEW && selectedUser)) {
data.comment = comment;
}

if (selectedOption !== HotspotStatusOptions.ADDITIONAL_REVIEW) {
data.resolution = HotspotResolution[selectedOption];
}
@@ -71,9 +83,8 @@ export default class HotspotActionsForm extends React.Component<Props, State> {
this.setState({ submitting: true });
return setSecurityHotspotStatus(hotspotKey, data)
.then(() => {
const { selectedUser } = this.state;
if (selectedOption === HotspotStatusOptions.ADDITIONAL_REVIEW && selectedUser) {
return this.assignHotspot(selectedUser);
return this.assignHotspot(selectedUser, comment);
}
return null;
})
@@ -85,22 +96,25 @@ export default class HotspotActionsForm extends React.Component<Props, State> {
});
};

assignHotspot = (assignee: T.UserActive) => {
assignHotspot = (assignee: T.UserActive, comment: string) => {
const { hotspotKey } = this.props;

return assignSecurityHotspot(hotspotKey, {
assignee: assignee.login
assignee: assignee.login,
comment
});
};

render() {
const { hotspotKey } = this.props;
const { selectedOption, selectedUser, submitting } = this.state;
const { comment, selectedOption, selectedUser, submitting } = this.state;

return (
<HotspotActionsFormRenderer
comment={comment}
hotspotKey={hotspotKey}
onAssign={this.handleAssign}
onChangeComment={this.handleCommentChange}
onSelectOption={this.handleSelectOption}
onSubmit={this.handleSubmit}
selectedOption={selectedOption}

+ 23
- 2
server/sonar-web/src/main/js/apps/securityHotspots/components/HotspotActionsFormRenderer.tsx View File

@@ -21,12 +21,15 @@ import * as React from 'react';
import { SubmitButton } from 'sonar-ui-common/components/controls/buttons';
import Radio from 'sonar-ui-common/components/controls/Radio';
import { translate } from 'sonar-ui-common/helpers/l10n';
import MarkdownTips from '../../../components/common/MarkdownTips';
import { HotspotStatusOptions } from '../../../types/security-hotspots';
import HotspotAssigneeSelect from './HotspotAssigneeSelect';

export interface HotspotActionsFormRendererProps {
comment: string;
hotspotKey: string;
onAssign: (user: T.UserActive) => void;
onChangeComment: (comment: string) => void;
onSelectOption: (option: HotspotStatusOptions) => void;
onSubmit: (event: React.SyntheticEvent<HTMLFormElement>) => void;
selectedOption: HotspotStatusOptions;
@@ -35,10 +38,10 @@ export interface HotspotActionsFormRendererProps {
}

export default function HotspotActionsFormRenderer(props: HotspotActionsFormRendererProps) {
const { selectedOption, submitting } = props;
const { comment, selectedOption, submitting } = props;

return (
<form className="abs-width-400" onSubmit={props.onSubmit}>
<form className="abs-width-400 padded" onSubmit={props.onSubmit}>
<h2>{translate('hotspots.form.title')}</h2>
<div className="display-flex-column big-spacer-bottom">
{renderOption({
@@ -63,6 +66,24 @@ export default function HotspotActionsFormRenderer(props: HotspotActionsFormRend
<HotspotAssigneeSelect onSelect={props.onAssign} />
</div>
)}
<div className="display-flex-column big-spacer-bottom">
<label className="little-spacer-bottom">{translate('hotspots.form.comment')}</label>
<textarea
className="form-field fixed-width spacer-bottom"
autoFocus={true}
onChange={(event: React.ChangeEvent<HTMLTextAreaElement>) =>
props.onChangeComment(event.currentTarget.value)
}
placeholder={
selectedOption === HotspotStatusOptions.SAFE
? translate('hotspots.form.comment.placeholder')
: ''
}
rows={6}
value={comment}
/>
<MarkdownTips />
</div>
<div className="text-right">
{submitting && <i className="spinner spacer-right" />}
<SubmitButton disabled={submitting}>{translate('hotspots.form.submit')}</SubmitButton>

+ 16
- 4
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/HotspotActionsForm-test.tsx View File

@@ -45,6 +45,12 @@ it('should handle option selection', () => {
expect(wrapper.state().selectedOption).toBe(HotspotStatusOptions.SAFE);
});

it('should handle comment change', () => {
const wrapper = shallowRender();
wrapper.instance().handleCommentChange('new comment');
expect(wrapper.state().comment).toBe('new comment');
});

it('should handle submit', async () => {
const onSubmit = jest.fn();
const wrapper = shallowRender({ onSubmit });
@@ -63,19 +69,21 @@ it('should handle submit', async () => {
expect(onSubmit).toBeCalled();

// SAFE
wrapper.setState({ selectedOption: HotspotStatusOptions.SAFE });
wrapper.setState({ comment: 'commentsafe', selectedOption: HotspotStatusOptions.SAFE });
await waitAndUpdate(wrapper);
await wrapper.instance().handleSubmit({ preventDefault } as any);
expect(setSecurityHotspotStatus).toBeCalledWith('key', {
comment: 'commentsafe',
status: HotspotStatus.REVIEWED,
resolution: HotspotResolution.SAFE
});

// FIXED
wrapper.setState({ selectedOption: HotspotStatusOptions.FIXED });
wrapper.setState({ comment: 'commentFixed', selectedOption: HotspotStatusOptions.FIXED });
await waitAndUpdate(wrapper);
await wrapper.instance().handleSubmit({ preventDefault } as any);
expect(setSecurityHotspotStatus).toBeCalledWith('key', {
comment: 'commentFixed',
status: HotspotStatus.REVIEWED,
resolution: HotspotResolution.FIXED
});
@@ -84,7 +92,10 @@ it('should handle submit', async () => {
it('should handle assignment', async () => {
const onSubmit = jest.fn();
const wrapper = shallowRender({ onSubmit });
wrapper.setState({ selectedOption: HotspotStatusOptions.ADDITIONAL_REVIEW });
wrapper.setState({
comment: 'assignment comment',
selectedOption: HotspotStatusOptions.ADDITIONAL_REVIEW
});
wrapper.instance().handleAssign(mockLoggedInUser({ login: 'userLogin' }));
await waitAndUpdate(wrapper);

@@ -97,7 +108,8 @@ it('should handle assignment', async () => {
status: HotspotStatus.TO_REVIEW
});
expect(assignSecurityHotspot).toBeCalledWith('key', {
assignee: 'userLogin'
assignee: 'userLogin',
comment: 'assignment comment'
});
expect(onSubmit).toBeCalled();
});

+ 2
- 0
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/HotspotActionsFormRenderer-test.tsx View File

@@ -43,8 +43,10 @@ it('should render correctly', () => {
function shallowRender(props: Partial<HotspotActionsFormRendererProps> = {}) {
return shallow<HotspotActionsForm>(
<HotspotActionsFormRenderer
comment="written comment"
hotspotKey="key"
onAssign={jest.fn()}
onChangeComment={jest.fn()}
onSelectOption={jest.fn()}
onSubmit={jest.fn()}
selectedOption={HotspotStatusOptions.FIXED}

+ 2
- 0
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/__snapshots__/HotspotActionsForm-test.tsx.snap View File

@@ -2,8 +2,10 @@

exports[`should render correctly 1`] = `
<HotspotActionsFormRenderer
comment=""
hotspotKey="key"
onAssign={[Function]}
onChangeComment={[Function]}
onSelectOption={[Function]}
onSubmit={[Function]}
selectedOption="FIXED"

+ 76
- 4
server/sonar-web/src/main/js/apps/securityHotspots/components/__tests__/__snapshots__/HotspotActionsFormRenderer-test.tsx.snap View File

@@ -2,7 +2,7 @@

exports[`should render correctly 1`] = `
<form
className="abs-width-400"
className="abs-width-400 padded"
onSubmit={[MockFunction]}
>
<h2>
@@ -66,6 +66,24 @@ exports[`should render correctly 1`] = `
</div>
</div>
</div>
<div
className="display-flex-column big-spacer-bottom"
>
<label
className="little-spacer-bottom"
>
hotspots.form.comment
</label>
<textarea
autoFocus={true}
className="form-field fixed-width spacer-bottom"
onChange={[Function]}
placeholder=""
rows={6}
value="written comment"
/>
<MarkdownTips />
</div>
<div
className="text-right"
>
@@ -80,7 +98,7 @@ exports[`should render correctly 1`] = `

exports[`should render correctly: Submitting 1`] = `
<form
className="abs-width-400"
className="abs-width-400 padded"
onSubmit={[MockFunction]}
>
<h2>
@@ -144,6 +162,24 @@ exports[`should render correctly: Submitting 1`] = `
</div>
</div>
</div>
<div
className="display-flex-column big-spacer-bottom"
>
<label
className="little-spacer-bottom"
>
hotspots.form.comment
</label>
<textarea
autoFocus={true}
className="form-field fixed-width spacer-bottom"
onChange={[Function]}
placeholder=""
rows={6}
value="written comment"
/>
<MarkdownTips />
</div>
<div
className="text-right"
>
@@ -161,7 +197,7 @@ exports[`should render correctly: Submitting 1`] = `

exports[`should render correctly: safe option selected 1`] = `
<form
className="abs-width-400"
className="abs-width-400 padded"
onSubmit={[MockFunction]}
>
<h2>
@@ -225,6 +261,24 @@ exports[`should render correctly: safe option selected 1`] = `
</div>
</div>
</div>
<div
className="display-flex-column big-spacer-bottom"
>
<label
className="little-spacer-bottom"
>
hotspots.form.comment
</label>
<textarea
autoFocus={true}
className="form-field fixed-width spacer-bottom"
onChange={[Function]}
placeholder="hotspots.form.comment.placeholder"
rows={6}
value="written comment"
/>
<MarkdownTips />
</div>
<div
className="text-right"
>
@@ -239,7 +293,7 @@ exports[`should render correctly: safe option selected 1`] = `

exports[`should render correctly: user selected 1`] = `
<form
className="abs-width-400"
className="abs-width-400 padded"
onSubmit={[MockFunction]}
>
<h2>
@@ -313,6 +367,24 @@ exports[`should render correctly: user selected 1`] = `
onSelect={[MockFunction]}
/>
</div>
<div
className="display-flex-column big-spacer-bottom"
>
<label
className="little-spacer-bottom"
>
hotspots.form.comment
</label>
<textarea
autoFocus={true}
className="form-field fixed-width spacer-bottom"
onChange={[Function]}
placeholder=""
rows={6}
value="written comment"
/>
<MarkdownTips />
</div>
<div
className="text-right"
>

+ 1
- 0
server/sonar-web/src/main/js/types/security-hotspots.ts View File

@@ -107,6 +107,7 @@ export interface HotspotSearchResponse {
export interface HotspotSetStatusRequest {
status: HotspotStatus;
resolution?: HotspotResolution;
comment?: string;
}

export interface HotspotAssignRequest {

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

@@ -675,7 +675,8 @@ hotspots.form.title=Mark Security Hotspot as:

hotspots.form.assign_to=Assign to:
hotspots.form.select_user=Select a user...
hotspots.form.comment=Comment
hotspots.form.comment=Comment:
hotspots.form.comment.placeholder=This status requires justification
hotspots.form.submit=Apply changes

hotspots.status_option.FIXED=Fixed

Loading…
Cancel
Save