diff options
Diffstat (limited to 'server/sonar-web/src/main/js/components/issue/popups')
22 files changed, 1239 insertions, 0 deletions
diff --git a/server/sonar-web/src/main/js/components/issue/popups/ChangelogPopup.js b/server/sonar-web/src/main/js/components/issue/popups/ChangelogPopup.js new file mode 100644 index 00000000000..49bdee8218e --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/ChangelogPopup.js @@ -0,0 +1,116 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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. + */ +// @flow +import React from 'react'; +import moment from 'moment'; +import { getIssueChangelog } from '../../../api/issues'; +import { translate } from '../../../helpers/l10n'; +import Avatar from '../../../components/ui/Avatar'; +import BubblePopup from '../../../components/common/BubblePopup'; +import IssueChangelogDiff from '../components/IssueChangelogDiff'; +import type { ChangelogDiff } from '../components/IssueChangelogDiff'; +import type { Issue } from '../types'; + +type Changelog = { + avatar?: string, + creationDate: string, + diffs: Array<ChangelogDiff>, + user: string, + userName: string +}; + +type Props = { + issue: Issue, + onFail: (Error) => void, + popupPosition?: {} +}; + +type State = { + changelogs: Array<Changelog> +}; + +export default class ChangelogPopup extends React.PureComponent { + mounted: boolean; + props: Props; + state: State = { + changelogs: [] + }; + + componentDidMount() { + this.mounted = true; + this.loadChangelog(); + } + + componentWillUnmount() { + this.mounted = false; + } + + loadChangelog() { + getIssueChangelog(this.props.issue.key).then( + changelogs => { + if (this.mounted) { + this.setState({ changelogs }); + } + }, + this.props.onFail + ); + } + + render() { + const { issue } = this.props; + const { author } = issue; + return ( + <BubblePopup position={this.props.popupPosition} customClass="bubble-popup-bottom-right"> + <div className="issue-changelog"> + <table className="spaced"> + <tbody> + <tr> + <td className="thin text-left text-top nowrap"> + {moment(issue.creationDate).format('LLL')} + </td> + <td className="thin text-left text-top nowrap" /> + <td className="text-left text-top"> + {author ? `${translate('created_by')} ${author}` : translate('created')} + </td> + </tr> + + {this.state.changelogs.map((item, idx) => ( + <tr key={idx}> + <td className="thin text-left text-top nowrap"> + {moment(item.creationDate).format('LLL')} + </td> + <td className="thin text-left text-top nowrap"> + {item.userName && + item.avatar && + <Avatar className="little-spacer-right" hash={item.avatar} size={16} />} + {item.userName} + </td> + <td className="text-left text-top"> + {item.diffs.map(diff => <IssueChangelogDiff key={diff.key} diff={diff} />)} + </td> + </tr> + ))} + </tbody> + </table> + </div> + </BubblePopup> + ); + } +} diff --git a/server/sonar-web/src/main/js/components/issue/popups/CommentDeletePopup.js b/server/sonar-web/src/main/js/components/issue/popups/CommentDeletePopup.js new file mode 100644 index 00000000000..3e06c95c07d --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/CommentDeletePopup.js @@ -0,0 +1,39 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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. + */ +// @flow +import React from 'react'; +import { translate } from '../../../helpers/l10n'; +import BubblePopup from '../../../components/common/BubblePopup'; + +type Props = { + onDelete: () => void, + popupPosition?: {} +}; + +export default function CommentDeletePopup(props: Props) { + return ( + <BubblePopup position={props.popupPosition} customClass="bubble-popup-bottom-right"> + <div className="text-right"> + <div className="spacer-bottom">{translate('issue.comment.delete_confirm_message')}</div> + <button className="button-red" onClick={props.onDelete}>{translate('delete')}</button> + </div> + </BubblePopup> + ); +} diff --git a/server/sonar-web/src/main/js/components/issue/popups/CommentPopup.js b/server/sonar-web/src/main/js/components/issue/popups/CommentPopup.js new file mode 100644 index 00000000000..cb079327d53 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/CommentPopup.js @@ -0,0 +1,113 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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. + */ +// @flow +import React from 'react'; +import classNames from 'classnames'; +import BubblePopup from '../../../components/common/BubblePopup'; +import MarkdownTips from '../../../components/common/MarkdownTips'; +import { translate } from '../../../helpers/l10n'; +import type { IssueComment } from '../types'; + +type Props = { + comment?: IssueComment, + customClass?: string, + onComment: (string) => void, + toggleComment: (boolean) => void, + placeholder: string, + popupPosition?: {} +}; + +type State = { + textComment: string +}; + +export default class CommentPopup extends React.PureComponent { + props: Props; + state: State; + + constructor(props: Props) { + super(props); + this.state = { + textComment: props.comment ? props.comment.markdown : '' + }; + } + + handleCommentChange = (evt: SyntheticInputEvent) => { + this.setState({ textComment: evt.target.value }); + }; + + handleCommentClick = () => { + if (this.state.textComment.trim().length > 0) { + this.props.onComment(this.state.textComment); + } + }; + + handleCancelClick = (evt: MouseEvent) => { + evt.preventDefault(); + this.props.toggleComment(false); + }; + + handleKeyboard = (evt: KeyboardEvent) => { + if (evt.keyCode === 13 && (evt.metaKey || evt.ctrlKey)) { + // Ctrl + Enter + this.handleCommentClick(); + } else if ([37, 38, 39, 40].includes(evt.keyCode)) { + // Arrow keys + evt.stopPropagation(); + } + }; + + render() { + const { comment } = this.props; + return ( + <BubblePopup + position={this.props.popupPosition} + customClass={classNames(this.props.customClass, 'bubble-popup-bottom-right')}> + <div className="issue-comment-form-text"> + <textarea + autoFocus={true} + placeholder={this.props.placeholder} + onChange={this.handleCommentChange} + onKeyDown={this.handleKeyboard} + value={this.state.textComment} + rows="2" + /> + </div> + <div className="issue-comment-form-footer"> + <div className="issue-comment-form-actions"> + <button + className="js-issue-comment-submit little-spacer-right" + disabled={this.state.textComment.trim().length < 1} + onClick={this.handleCommentClick}> + {comment && translate('save')} + {!comment && translate('issue.comment.submit')} + </button> + <a href="#" className="js-issue-comment-cancel" onClick={this.handleCancelClick}> + {translate('cancel')} + </a> + </div> + <div className="issue-comment-form-tips"> + <MarkdownTips /> + </div> + </div> + </BubblePopup> + ); + } +} diff --git a/server/sonar-web/src/main/js/components/issue/popups/SetAssigneePopup.js b/server/sonar-web/src/main/js/components/issue/popups/SetAssigneePopup.js new file mode 100644 index 00000000000..c700c3a341f --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/SetAssigneePopup.js @@ -0,0 +1,167 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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. + */ +// @flow +import React from 'react'; +import classNames from 'classnames'; +import { css } from 'glamor'; +import { debounce, map } from 'lodash'; +import Avatar from '../../../components/ui/Avatar'; +import BubblePopup from '../../../components/common/BubblePopup'; +import SelectList from '../../../components/common/SelectList'; +import SelectListItem from '../../../components/common/SelectListItem'; +import getCurrentUserFromStore from '../../../app/utils/getCurrentUserFromStore'; +import { areThereCustomOrganizations } from '../../../store/organizations/utils'; +import { searchMembers } from '../../../api/organizations'; +import { searchUsers } from '../../../api/users'; +import { translate } from '../../../helpers/l10n'; +import type { Issue } from '../types'; + +type User = { + avatar?: string, + email?: string, + login: string, + name: string +}; + +type Props = { + issue: Issue, + onFail: (Error) => void, + onSelect: (string) => void, + popupPosition?: {} +}; + +type State = { + query: string, + users: Array<User>, + currentUser: string +}; + +const LIST_SIZE = 10; +const USER_MARGIN = css({ marginLeft: '24px' }); + +export default class SetAssigneePopup extends React.PureComponent { + defaultUsersArray: Array<User>; + organizationEnabled: boolean; + props: Props; + state: State; + + constructor(props: Props) { + super(props); + this.organizationEnabled = areThereCustomOrganizations(); + this.searchUsers = debounce(this.searchUsers, 250); + this.searchMembers = debounce(this.searchMembers, 250); + this.defaultUsersArray = [{ login: '', name: translate('unassigned') }]; + + const currentUser = getCurrentUserFromStore(); + if (currentUser != null) { + this.defaultUsersArray = [currentUser, ...this.defaultUsersArray]; + } + + this.state = { + query: '', + users: this.defaultUsersArray, + currentUser: currentUser.login + }; + } + + searchMembers = (query: string) => { + searchMembers({ + organization: this.props.issue.projectOrganization, + q: query, + ps: LIST_SIZE + }).then(this.handleSearchResult, this.props.onFail); + }; + + searchUsers = (query: string) => { + searchUsers(query, LIST_SIZE).then(this.handleSearchResult, this.props.onFail); + }; + + handleSearchResult = (data: Object) => { + this.setState({ + users: data.users, + currentUser: data.users.length > 0 ? data.users[0].login : '' + }); + }; + + handleSearchChange = (evt: SyntheticInputEvent) => { + const query = evt.target.value; + if (query.length < 2) { + this.setState({ + query, + users: this.defaultUsersArray, + currentUser: this.defaultUsersArray[0].login + }); + } else { + this.setState({ query }); + if (this.organizationEnabled) { + this.searchMembers(query); + } else { + this.searchUsers(query); + } + } + }; + + render() { + return ( + <BubblePopup + position={this.props.popupPosition} + customClass="bubble-popup-menu bubble-popup-bottom"> + <div className="multi-select"> + <div className="search-box menu-search"> + <button className="search-box-submit button-clean"> + <i className="icon-search-new" /> + </button> + <input + type="search" + value={this.state.query} + className="search-box-input" + placeholder={translate('search_verb')} + onChange={this.handleSearchChange} + autoComplete="off" + autoFocus={true} + /> + </div> + <SelectList + items={map(this.state.users, 'login')} + currentItem={this.state.currentUser} + onSelect={this.props.onSelect}> + {this.state.users.map(user => ( + <SelectListItem key={user.login} item={user.login}> + {(user.avatar || user.email) && + <Avatar + className="spacer-right" + email={user.email} + hash={user.avatar} + size={16} + />} + <span + className={classNames('vertical-middle', { + [USER_MARGIN]: !(user.avatar || user.email) + })}> + {user.name} + </span> + </SelectListItem> + ))} + </SelectList> + </div> + </BubblePopup> + ); + } +} diff --git a/server/sonar-web/src/main/js/components/issue/popups/SetIssueTagsPopup.js b/server/sonar-web/src/main/js/components/issue/popups/SetIssueTagsPopup.js new file mode 100644 index 00000000000..f8584f59f07 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/SetIssueTagsPopup.js @@ -0,0 +1,86 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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. + */ +//@flow +import React from 'react'; +import { debounce, without } from 'lodash'; +import TagsSelector from '../../../components/tags/TagsSelector'; +import { searchIssueTags } from '../../../api/issues'; + +type Props = { + popupPosition?: {}, + onFail: (Error) => void, + selectedTags: Array<string>, + setTags: (Array<string>) => void +}; + +type State = { + searchResult: Array<string> +}; + +const LIST_SIZE = 10; + +export default class SetIssueTagsPopup extends React.PureComponent { + props: Props; + state: State; + + constructor(props: Props) { + super(props); + this.state = { searchResult: [] }; + this.onSearch = debounce(this.onSearch, 250); + } + + componentDidMount() { + this.onSearch(''); + } + + onSearch = (query: string) => { + searchIssueTags({ + q: query || '', + ps: Math.min(this.props.selectedTags.length - 1 + LIST_SIZE, 100) + }).then( + (tags: Array<string>) => { + this.setState({ searchResult: tags }); + }, + this.props.onFail + ); + }; + + onSelect = (tag: string) => { + this.props.setTags([...this.props.selectedTags, tag]); + }; + + onUnselect = (tag: string) => { + this.props.setTags(without(this.props.selectedTags, tag)); + }; + + render() { + return ( + <TagsSelector + position={this.props.popupPosition} + tags={this.state.searchResult} + selectedTags={this.props.selectedTags} + listSize={LIST_SIZE} + onSearch={this.onSearch} + onSelect={this.onSelect} + onUnselect={this.onUnselect} + /> + ); + } +} diff --git a/server/sonar-web/src/main/js/components/issue/popups/SetSeverityPopup.js b/server/sonar-web/src/main/js/components/issue/popups/SetSeverityPopup.js new file mode 100644 index 00000000000..bef971615d5 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/SetSeverityPopup.js @@ -0,0 +1,59 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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. + */ +// @flow +import React from 'react'; +import { translate } from '../../../helpers/l10n'; +import BubblePopup from '../../../components/common/BubblePopup'; +import SelectList from '../../../components/common/SelectList'; +import SelectListItem from '../../../components/common/SelectListItem'; +import SeverityIcon from '../../../components/shared/SeverityIcon'; +import type { Issue } from '../types'; + +type Props = { + issue: Issue, + onSelect: (string) => void, + popupPosition?: {} +}; + +const SEVERITY = ['BLOCKER', 'CRITICAL', 'MAJOR', 'MINOR', 'INFO']; + +export default class SetSeverityPopup extends React.PureComponent { + props: Props; + + render() { + return ( + <BubblePopup + position={this.props.popupPosition} + customClass="bubble-popup-menu bubble-popup-bottom"> + <SelectList + items={SEVERITY} + currentItem={this.props.issue.severity} + onSelect={this.props.onSelect}> + {SEVERITY.map(severity => ( + <SelectListItem key={severity} item={severity}> + <SeverityIcon className="little-spacer-right" severity={severity} /> + {translate('severity', severity)} + </SelectListItem> + ))} + </SelectList> + </BubblePopup> + ); + } +} diff --git a/server/sonar-web/src/main/js/components/issue/popups/SetTransitionPopup.js b/server/sonar-web/src/main/js/components/issue/popups/SetTransitionPopup.js new file mode 100644 index 00000000000..d1c83099551 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/SetTransitionPopup.js @@ -0,0 +1,57 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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. + */ +// @flow +import React from 'react'; +import BubblePopup from '../../../components/common/BubblePopup'; +import SelectList from '../../../components/common/SelectList'; +import SelectListItem from '../../../components/common/SelectListItem'; +import { translate } from '../../../helpers/l10n'; + +type Props = { + transitions: Array<string>, + onSelect: (string) => void, + popupPosition?: {} +}; + +export default class SetTransitionPopup extends React.PureComponent { + props: Props; + + render() { + const { transitions } = this.props; + return ( + <BubblePopup + position={this.props.popupPosition} + customClass="bubble-popup-menu bubble-popup-bottom"> + <SelectList items={transitions} currentItem={transitions[0]} onSelect={this.props.onSelect}> + {transitions.map(transition => { + return ( + <SelectListItem + key={transition} + item={transition} + title={translate('issue.transition', transition, 'description')}> + {translate('issue.transition', transition)} + </SelectListItem> + ); + })} + </SelectList> + </BubblePopup> + ); + } +} diff --git a/server/sonar-web/src/main/js/components/issue/popups/SetTypePopup.js b/server/sonar-web/src/main/js/components/issue/popups/SetTypePopup.js new file mode 100644 index 00000000000..7bd10d57ede --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/SetTypePopup.js @@ -0,0 +1,59 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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. + */ +// @flow +import React from 'react'; +import { translate } from '../../../helpers/l10n'; +import BubblePopup from '../../../components/common/BubblePopup'; +import IssueTypeIcon from '../../../components/ui/IssueTypeIcon'; +import SelectList from '../../../components/common/SelectList'; +import SelectListItem from '../../../components/common/SelectListItem'; +import type { Issue } from '../types'; + +type Props = { + issue: Issue, + onSelect: (string) => void, + popupPosition?: {} +}; + +const TYPES = ['BUG', 'VULNERABILITY', 'CODE_SMELL']; + +export default class SetTypePopup extends React.PureComponent { + props: Props; + + render() { + return ( + <BubblePopup + position={this.props.popupPosition} + customClass="bubble-popup-menu bubble-popup-bottom"> + <SelectList + items={TYPES} + currentItem={this.props.issue.type} + onSelect={this.props.onSelect}> + {TYPES.map(type => ( + <SelectListItem key={type} item={type}> + <IssueTypeIcon className="little-spacer-right" query={type} /> + {translate('issue.type', type)} + </SelectListItem> + ))} + </SelectList> + </BubblePopup> + ); + } +} diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/ChangelogPopup-test.js b/server/sonar-web/src/main/js/components/issue/popups/__tests__/ChangelogPopup-test.js new file mode 100644 index 00000000000..6c4f9d5977e --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/ChangelogPopup-test.js @@ -0,0 +1,46 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 { shallow } from 'enzyme'; +import React from 'react'; +import ChangelogPopup from '../ChangelogPopup'; + +it('should render the changelog popup correctly', () => { + const element = shallow( + <ChangelogPopup + issue={{ + key: 'issuekey', + author: 'john.david.dalton@gmail.com', + creationDate: '2017-03-01T09:36:01+0100' + }} + onFail={jest.fn()} + /> + ); + element.setState({ + changelogs: [ + { + creationDate: '2017-03-01T09:36:01+0100', + userName: 'john.doe', + avatar: 'gravatarhash', + diffs: [{ key: 'severity', newValue: 'MINOR', oldValue: 'CRITICAL' }] + } + ] + }); + expect(element).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/CommentDeletePopup-test.js b/server/sonar-web/src/main/js/components/issue/popups/__tests__/CommentDeletePopup-test.js new file mode 100644 index 00000000000..64a42433849 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/CommentDeletePopup-test.js @@ -0,0 +1,31 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 { shallow } from 'enzyme'; +import React from 'react'; +import CommentDeletePopup from '../CommentDeletePopup'; +import { click } from '../../../../helpers/testUtils'; + +it('should render the comment delete popup correctly', () => { + const onDelete = jest.fn(); + const element = shallow(<CommentDeletePopup onDelete={onDelete} />); + expect(element).toMatchSnapshot(); + click(element.find('button')); + expect(onDelete.mock.calls.length).toBe(1); +}); diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/CommentPopup-test.js b/server/sonar-web/src/main/js/components/issue/popups/__tests__/CommentPopup-test.js new file mode 100644 index 00000000000..2eac23a4e32 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/CommentPopup-test.js @@ -0,0 +1,66 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 { shallow } from 'enzyme'; +import React from 'react'; +import CommentPopup from '../CommentPopup'; +import { click } from '../../../../helpers/testUtils'; + +it('should render the comment popup correctly without existing comment', () => { + const element = shallow( + <CommentPopup + onComment={jest.fn()} + toggleComment={jest.fn()} + placeholder="placeholder test" + customClass="myclass" + /> + ); + expect(element).toMatchSnapshot(); +}); + +it('should render the comment popup correctly when changing a comment', () => { + const element = shallow( + <CommentPopup + comment={{ + markdown: '*test*' + }} + onComment={jest.fn()} + toggleComment={jest.fn()} + placeholder="" + /> + ); + expect(element).toMatchSnapshot(); +}); + +it('should render not allow to send comment with only spaces', () => { + const onComment = jest.fn(); + const element = shallow( + <CommentPopup + onComment={onComment} + toggleComment={jest.fn()} + placeholder="placeholder test" + customClass="myclass" + /> + ); + click(element.find('button.js-issue-comment-submit')); + expect(onComment.mock.calls.length).toBe(0); + element.setState({ textComment: 'mycomment' }); + click(element.find('button.js-issue-comment-submit')); + expect(onComment.mock.calls.length).toBe(1); +}); diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetIssueTagsPopup-test.js b/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetIssueTagsPopup-test.js new file mode 100644 index 00000000000..c9d1c0a4e23 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetIssueTagsPopup-test.js @@ -0,0 +1,30 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 { shallow } from 'enzyme'; +import React from 'react'; +import SetIssueTagsPopup from '../SetIssueTagsPopup'; + +it('should render tags popup correctly', () => { + const element = shallow( + <SetIssueTagsPopup onFail={jest.fn()} selectedTags="mytag" setTags={jest.fn()} /> + ); + element.setState({ searchResult: ['mytag', 'test', 'second'] }); + expect(element).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetSeverityPopup-test.js b/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetSeverityPopup-test.js new file mode 100644 index 00000000000..ed0f67680b8 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetSeverityPopup-test.js @@ -0,0 +1,27 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 { shallow } from 'enzyme'; +import React from 'react'; +import SetSeverityPopup from '../SetSeverityPopup'; + +it('should render tags popup correctly', () => { + const element = shallow(<SetSeverityPopup issue={{ severity: 'MAJOR' }} onSelect={jest.fn()} />); + expect(element).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetTransitionPopup-test.js b/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetTransitionPopup-test.js new file mode 100644 index 00000000000..4d54d66b413 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetTransitionPopup-test.js @@ -0,0 +1,32 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 { shallow } from 'enzyme'; +import React from 'react'; +import SetTransitionPopup from '../SetTransitionPopup'; + +it('should render tags popup correctly', () => { + const element = shallow( + <SetTransitionPopup + onSelect={jest.fn()} + transitions={['confirm', 'resolve', 'falsepositive', 'wontfix']} + /> + ); + expect(element).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetTypePopup-test.js b/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetTypePopup-test.js new file mode 100644 index 00000000000..70b7f7a95a6 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/SetTypePopup-test.js @@ -0,0 +1,27 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 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 { shallow } from 'enzyme'; +import React from 'react'; +import SetTypePopup from '../SetTypePopup'; + +it('should render tags popup correctly', () => { + const element = shallow(<SetTypePopup issue={{ type: 'BUG' }} onSelect={jest.fn()} />); + expect(element).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/ChangelogPopup-test.js.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/ChangelogPopup-test.js.snap new file mode 100644 index 00000000000..aa5c866b59c --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/ChangelogPopup-test.js.snap @@ -0,0 +1,50 @@ +exports[`test should render the changelog popup correctly 1`] = ` +<BubblePopup + customClass="bubble-popup-bottom-right"> + <div + className="issue-changelog"> + <table + className="spaced"> + <tbody> + <tr> + <td + className="thin text-left text-top nowrap"> + March 1, 2017 9:36 AM + </td> + <td + className="thin text-left text-top nowrap" /> + <td + className="text-left text-top"> + created_by john.david.dalton@gmail.com + </td> + </tr> + <tr> + <td + className="thin text-left text-top nowrap"> + March 1, 2017 9:36 AM + </td> + <td + className="thin text-left text-top nowrap"> + <Connect(Avatar) + className="little-spacer-right" + hash="gravatarhash" + size={16} /> + john.doe + </td> + <td + className="text-left text-top"> + <IssueChangelogDiff + diff={ + Object { + "key": "severity", + "newValue": "MINOR", + "oldValue": "CRITICAL", + } + } /> + </td> + </tr> + </tbody> + </table> + </div> +</BubblePopup> +`; diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/CommentDeletePopup-test.js.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/CommentDeletePopup-test.js.snap new file mode 100644 index 00000000000..23e0864e519 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/CommentDeletePopup-test.js.snap @@ -0,0 +1,17 @@ +exports[`test should render the comment delete popup correctly 1`] = ` +<BubblePopup + customClass="bubble-popup-bottom-right"> + <div + className="text-right"> + <div + className="spacer-bottom"> + issue.comment.delete_confirm_message + </div> + <button + className="button-red" + onClick={[Function]}> + delete + </button> + </div> +</BubblePopup> +`; diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/CommentPopup-test.js.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/CommentPopup-test.js.snap new file mode 100644 index 00000000000..b4ab112f14f --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/CommentPopup-test.js.snap @@ -0,0 +1,75 @@ +exports[`test should render the comment popup correctly when changing a comment 1`] = ` +<BubblePopup + customClass="bubble-popup-bottom-right"> + <div + className="issue-comment-form-text"> + <textarea + autoFocus={true} + onChange={[Function]} + onKeyDown={[Function]} + placeholder="" + rows="2" + value="*test*" /> + </div> + <div + className="issue-comment-form-footer"> + <div + className="issue-comment-form-actions"> + <button + className="js-issue-comment-submit little-spacer-right" + disabled={false} + onClick={[Function]}> + save + </button> + <a + className="js-issue-comment-cancel" + href="#" + onClick={[Function]}> + cancel + </a> + </div> + <div + className="issue-comment-form-tips"> + <MarkdownTips /> + </div> + </div> +</BubblePopup> +`; + +exports[`test should render the comment popup correctly without existing comment 1`] = ` +<BubblePopup + customClass="myclass bubble-popup-bottom-right"> + <div + className="issue-comment-form-text"> + <textarea + autoFocus={true} + onChange={[Function]} + onKeyDown={[Function]} + placeholder="placeholder test" + rows="2" + value="" /> + </div> + <div + className="issue-comment-form-footer"> + <div + className="issue-comment-form-actions"> + <button + className="js-issue-comment-submit little-spacer-right" + disabled={true} + onClick={[Function]}> + issue.comment.submit + </button> + <a + className="js-issue-comment-cancel" + href="#" + onClick={[Function]}> + cancel + </a> + </div> + <div + className="issue-comment-form-tips"> + <MarkdownTips /> + </div> + </div> +</BubblePopup> +`; diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetIssueTagsPopup-test.js.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetIssueTagsPopup-test.js.snap new file mode 100644 index 00000000000..91a5c0b4d6a --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetIssueTagsPopup-test.js.snap @@ -0,0 +1,15 @@ +exports[`test should render tags popup correctly 1`] = ` +<TagsSelector + listSize={10} + onSearch={[Function]} + onSelect={[Function]} + onUnselect={[Function]} + selectedTags="mytag" + tags={ + Array [ + "mytag", + "test", + "second", + ] + } /> +`; diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetSeverityPopup-test.js.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetSeverityPopup-test.js.snap new file mode 100644 index 00000000000..0c329d01adc --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetSeverityPopup-test.js.snap @@ -0,0 +1,53 @@ +exports[`test should render tags popup correctly 1`] = ` +<BubblePopup + customClass="bubble-popup-menu bubble-popup-bottom"> + <SelectList + currentItem="MAJOR" + items={ + Array [ + "BLOCKER", + "CRITICAL", + "MAJOR", + "MINOR", + "INFO", + ] + } + onSelect={[Function]}> + <SelectListItem + item="BLOCKER"> + <SeverityIcon + className="little-spacer-right" + severity="BLOCKER" /> + severity.BLOCKER + </SelectListItem> + <SelectListItem + item="CRITICAL"> + <SeverityIcon + className="little-spacer-right" + severity="CRITICAL" /> + severity.CRITICAL + </SelectListItem> + <SelectListItem + item="MAJOR"> + <SeverityIcon + className="little-spacer-right" + severity="MAJOR" /> + severity.MAJOR + </SelectListItem> + <SelectListItem + item="MINOR"> + <SeverityIcon + className="little-spacer-right" + severity="MINOR" /> + severity.MINOR + </SelectListItem> + <SelectListItem + item="INFO"> + <SeverityIcon + className="little-spacer-right" + severity="INFO" /> + severity.INFO + </SelectListItem> + </SelectList> +</BubblePopup> +`; diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetTransitionPopup-test.js.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetTransitionPopup-test.js.snap new file mode 100644 index 00000000000..08072d269b9 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetTransitionPopup-test.js.snap @@ -0,0 +1,37 @@ +exports[`test should render tags popup correctly 1`] = ` +<BubblePopup + customClass="bubble-popup-menu bubble-popup-bottom"> + <SelectList + currentItem="confirm" + items={ + Array [ + "confirm", + "resolve", + "falsepositive", + "wontfix", + ] + } + onSelect={[Function]}> + <SelectListItem + item="confirm" + title="issue.transition.confirm.description"> + issue.transition.confirm + </SelectListItem> + <SelectListItem + item="resolve" + title="issue.transition.resolve.description"> + issue.transition.resolve + </SelectListItem> + <SelectListItem + item="falsepositive" + title="issue.transition.falsepositive.description"> + issue.transition.falsepositive + </SelectListItem> + <SelectListItem + item="wontfix" + title="issue.transition.wontfix.description"> + issue.transition.wontfix + </SelectListItem> + </SelectList> +</BubblePopup> +`; diff --git a/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetTypePopup-test.js.snap b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetTypePopup-test.js.snap new file mode 100644 index 00000000000..a6719f89bf5 --- /dev/null +++ b/server/sonar-web/src/main/js/components/issue/popups/__tests__/__snapshots__/SetTypePopup-test.js.snap @@ -0,0 +1,37 @@ +exports[`test should render tags popup correctly 1`] = ` +<BubblePopup + customClass="bubble-popup-menu bubble-popup-bottom"> + <SelectList + currentItem="BUG" + items={ + Array [ + "BUG", + "VULNERABILITY", + "CODE_SMELL", + ] + } + onSelect={[Function]}> + <SelectListItem + item="BUG"> + <IssueTypeIcon + className="little-spacer-right" + query="BUG" /> + issue.type.BUG + </SelectListItem> + <SelectListItem + item="VULNERABILITY"> + <IssueTypeIcon + className="little-spacer-right" + query="VULNERABILITY" /> + issue.type.VULNERABILITY + </SelectListItem> + <SelectListItem + item="CODE_SMELL"> + <IssueTypeIcon + className="little-spacer-right" + query="CODE_SMELL" /> + issue.type.CODE_SMELL + </SelectListItem> + </SelectList> +</BubblePopup> +`; |