* 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, keyBy, times } from 'lodash';
+import { cloneDeep, keyBy, times, uniqueId } from 'lodash';
import { RuleDescriptionSections } from '../../apps/coding-rules/rule';
+import { mockIssueChangelog } from '../../helpers/mocks/issues';
import { mockSnippetsByComponent } from '../../helpers/mocks/sources';
import { RequestData } from '../../helpers/request';
import { getStandards } from '../../helpers/security-standard';
import {
- mockCurrentUser,
mockLoggedInUser,
mockPaging,
mockRawIssue,
} from '../../helpers/testMocks';
import {
ASSIGNEE_ME,
+ IssueActions,
IssueResolution,
IssueScope,
IssueSeverity,
IssueStatus,
+ IssueTransition,
IssueType,
RawFacet,
RawIssue,
RuleDetails,
SnippetsByComponent,
} from '../../types/types';
-import { NoticeType } from '../../types/users';
+import { LoggedInUser, NoticeType } from '../../types/users';
import {
addIssueComment,
bulkChangeIssues,
deleteIssueComment,
editIssueComment,
+ getIssueChangelog,
getIssueFlowSnippets,
searchIssues,
searchIssueTags,
export default class IssuesServiceMock {
isAdmin = false;
+ currentUser: LoggedInUser;
standards?: Standards;
defaultList: IssueData[];
list: IssueData[];
constructor() {
+ this.currentUser = mockLoggedInUser();
this.defaultList = [
{
issue: mockRawIssue(false, {
},
{
issue: mockRawIssue(false, {
- actions: ['set_type', 'set_tags', 'comment', 'set_severity', 'assign'],
+ actions: Object.values(IssueActions),
transitions: ['confirm', 'resolve', 'falsepositive', 'wontfix'],
key: 'issue2',
component: 'foo:test2.js',
},
{
issue: mockRawIssue(false, {
- actions: ['set_type', 'set_tags', 'comment', 'set_severity', 'assign'],
+ actions: Object.values(IssueActions),
transitions: ['confirm', 'resolve', 'falsepositive', 'wontfix'],
key: 'issue4',
component: 'foo:test2.js',
(getCurrentUser as jest.Mock).mockImplementation(this.handleGetCurrentUser);
(dismissNotice as jest.Mock).mockImplementation(this.handleDismissNotification);
(setIssueType as jest.Mock).mockImplementation(this.handleSetIssueType);
- (setIssueAssignee as jest.Mock).mockImplementation(this.handleSetIssueAssignee);
+ jest.mocked(setIssueAssignee).mockImplementation(this.handleSetIssueAssignee);
(setIssueSeverity as jest.Mock).mockImplementation(this.handleSetIssueSeverity);
(setIssueTransition as jest.Mock).mockImplementation(this.handleSetIssueTransition);
(setIssueTags as jest.Mock).mockImplementation(this.handleSetIssueTags);
- (addIssueComment as jest.Mock).mockImplementation(this.handleAddComment);
- (editIssueComment as jest.Mock).mockImplementation(this.handleEditComment);
- (deleteIssueComment as jest.Mock).mockImplementation(this.handleDeleteComment);
+ jest.mocked(addIssueComment).mockImplementation(this.handleAddComment);
+ jest.mocked(editIssueComment).mockImplementation(this.handleEditComment);
+ jest.mocked(deleteIssueComment).mockImplementation(this.handleDeleteComment);
(searchUsers as jest.Mock).mockImplementation(this.handleSearchUsers);
(searchIssueTags as jest.Mock).mockImplementation(this.handleSearchIssueTags);
+ jest.mocked(getIssueChangelog).mockImplementation(this.handleGetIssueChangelog);
}
reset = () => {
this.list = cloneDeep(this.defaultList);
+ this.currentUser = mockLoggedInUser();
+ };
+
+ setCurrentUser = (user: LoggedInUser) => {
+ this.currentUser = user;
+ };
+
+ setIssueList = (list: IssueData[]) => {
+ this.list = list;
};
async getStandards(): Promise<Standards> {
};
handleGetCurrentUser = () => {
- return this.reply(mockCurrentUser());
+ return this.reply(this.currentUser);
};
handleDismissNotification = (noticeType: NoticeType) => {
};
handleSetIssueAssignee = (data: { issue: string; assignee?: string }) => {
- return this.getActionsResponse({ assignee: data.assignee }, data.issue);
+ return this.getActionsResponse(
+ { assignee: data.assignee === '_me' ? this.currentUser.login : data.assignee },
+ data.issue
+ );
};
handleSetIssueTransition = (data: { issue: string; transition: string }) => {
- const statusMap: { [key: string]: string } = {
- confirm: 'CONFIRMED',
- unconfirm: 'REOPENED',
- resolve: 'RESOLVED',
- wontfix: 'RESOLVED',
- falsepositive: 'RESOLVED',
+ const statusMap: { [key: string]: IssueStatus } = {
+ [IssueTransition.Confirm]: IssueStatus.Confirmed,
+ [IssueTransition.UnConfirm]: IssueStatus.Reopened,
+ [IssueTransition.Resolve]: IssueStatus.Resolved,
+ [IssueTransition.WontFix]: IssueStatus.Resolved,
+ [IssueTransition.FalsePositive]: IssueStatus.Resolved,
};
- const transitionMap: Dict<string[]> = {
- REOPENED: ['confirm', 'resolve', 'falsepositive', 'wontfix'],
- OPEN: ['confirm', 'resolve', 'falsepositive', 'wontfix'],
- CONFIRMED: ['resolve', 'unconfirm', 'falsepositive', 'wontfix'],
- RESOLVED: ['reopen'],
+ const transitionMap: Dict<IssueTransition[]> = {
+ [IssueStatus.Reopened]: [
+ IssueTransition.Confirm,
+ IssueTransition.Resolve,
+ IssueTransition.FalsePositive,
+ IssueTransition.WontFix,
+ ],
+ [IssueStatus.Open]: [
+ IssueTransition.Confirm,
+ IssueTransition.Resolve,
+ IssueTransition.FalsePositive,
+ IssueTransition.WontFix,
+ ],
+ [IssueStatus.Confirmed]: [
+ IssueTransition.Resolve,
+ IssueTransition.UnConfirm,
+ IssueTransition.FalsePositive,
+ IssueTransition.WontFix,
+ ],
+ [IssueStatus.Resolved]: [IssueTransition.Reopen],
};
const resolutionMap: Dict<string> = {
- wontfix: IssueResolution.WontFix,
- falsepositive: IssueResolution.FalsePositive,
+ [IssueTransition.WontFix]: IssueResolution.WontFix,
+ [IssueTransition.FalsePositive]: IssueResolution.FalsePositive,
};
return this.getActionsResponse(
};
handleAddComment = (data: { issue: string; text: string }) => {
- // For comment its little more complex to get comment Id
return this.getActionsResponse(
{
comments: [
{
createdAt: '2022-07-28T11:30:04+0200',
htmlText: data.text,
- key: '1234',
+ key: uniqueId(),
login: 'admin',
markdown: data.text,
updatable: true,
};
handleEditComment = (data: { comment: string; text: string }) => {
- // For comment its little more complex to get comment Id
+ const issueKey = this.list.find((i) => i.issue.comments?.some((c) => c.key === data.comment))
+ ?.issue.key;
+ if (!issueKey) {
+ throw new Error(`Couldn't find issue for comment ${data.comment}`);
+ }
return this.getActionsResponse(
{
comments: [
{
createdAt: '2022-07-28T11:30:04+0200',
htmlText: data.text,
- key: '1234',
+ key: data.comment,
login: 'admin',
markdown: data.text,
updatable: true,
},
],
},
- 'issue2'
+ issueKey
);
};
- handleDeleteComment = () => {
- // For comment its little more complex to get comment Id
+ handleDeleteComment = (data: { comment: string }) => {
+ const issue = this.list.find((i) =>
+ i.issue.comments?.some((c) => c.key === data.comment)
+ )?.issue;
+ if (!issue) {
+ throw new Error(`Couldn't find issue for comment ${data.comment}`);
+ }
return this.getActionsResponse(
{
- comments: [],
+ comments: issue.comments?.filter((c) => c.key !== data.comment),
},
- 'issue2'
+ issue.key
);
};
return this.reply(['accessibility', 'android']);
};
+ handleGetIssueChangelog = (_issue: string) => {
+ return this.reply({
+ changelog: [
+ mockIssueChangelog({
+ creationDate: '2018-09-01',
+ diffs: [
+ {
+ key: 'status',
+ newValue: IssueStatus.Reopened,
+ oldValue: IssueStatus.Confirmed,
+ },
+ ],
+ }),
+ mockIssueChangelog({
+ creationDate: '2018-10-01',
+ diffs: [
+ {
+ key: 'assign',
+ newValue: 'darth.vader',
+ oldValue: 'luke.skywalker',
+ },
+ ],
+ }),
+ ],
+ });
+ };
+
getActionsResponse = (overrides: Partial<RawIssue>, issueKey: string) => {
const issueDataSelected = this.list.find((l) => l.issue.key === issueKey);
// Improve this to include all the bulk change fonctionality
it('should be able to bulk change', async () => {
const user = userEvent.setup();
+ const currentUser = mockLoggedInUser();
issuesHandler.setIsAdmin(true);
- renderIssueApp(mockLoggedInUser());
+ issuesHandler.setCurrentUser(currentUser);
+ renderIssueApp(currentUser);
// Check that the bulk button has correct behavior
expect(screen.getByRole('button', { name: 'bulk_change' })).toBeDisabled();
it('should allow to set creation date', async () => {
const user = userEvent.setup();
- renderIssueApp(mockLoggedInUser());
+ const currentUser = mockLoggedInUser();
+ issuesHandler.setCurrentUser(currentUser);
+ renderIssueApp(currentUser);
await waitOnDataLoaded();
// Select a specific date range such that only one issue matches
it('should allow to only show my issues', async () => {
const user = userEvent.setup();
- renderIssueApp(mockLoggedInUser());
+ const currentUser = mockLoggedInUser();
+ issuesHandler.setCurrentUser(currentUser);
+ renderIssueApp(currentUser);
await waitOnDataLoaded();
// By default, it should show all issues
expect(
screen.getByRole('heading', {
- name: 'Issue with tags sonar-lint-icon issue.resolution.badge.DEPRECATED',
+ name: 'Issue with tags issue.quick_fix_available_with_sonarlint_no_link issue.resolution.badge.DEPRECATED',
})
).toBeInTheDocument();
});
} from '../../../helpers/measures';
import { getBranchLikeUrl } from '../../../helpers/urls';
import { BranchLike } from '../../../types/branch-like';
-import { IssueType as IssueTypeEnum } from '../../../types/issues';
+import { IssueSeverity, IssueType as IssueTypeEnum } from '../../../types/issues';
+import { MetricType } from '../../../types/metrics';
import { FacetValue, IssueType, MeasureEnhanced, SourceViewerFile } from '../../../types/types';
import Measure from '../../measure/Measure';
import SeverityHelper from '../../shared/SeverityHelper';
<IssueTypeIcon className="little-spacer-right" query={f.val} />
{translate('issue.type', f.val)}
</span>
- <span className="measure-value">{formatMeasure(f.count, 'SHORT_INT')}</span>
+ <span className="measure-value">
+ {formatMeasure(f.count, MetricType.ShortInteger)}
+ </span>
</div>
)
)}
)}
{severitiesFacet && (
<div className="measures">
- {sortBy(severitiesFacet, (f) => SEVERITIES.indexOf(f.val)).map((f) => (
- <div className="measure measure-one-line" key={f.val}>
- <span className="measure-name">
- <SeverityHelper severity={f.val} />
- </span>
- <span className="measure-value">{formatMeasure(f.count, 'SHORT_INT')}</span>
- </div>
- ))}
+ {sortBy(severitiesFacet, (f) => SEVERITIES.indexOf(f.val as IssueSeverity)).map(
+ (f) => (
+ <div className="measure measure-one-line" key={f.val}>
+ <span className="measure-name">
+ <SeverityHelper severity={f.val} />
+ </span>
+ <span className="measure-value">
+ {formatMeasure(f.count, MetricType.ShortInteger)}
+ </span>
+ </div>
+ )
+ )}
</div>
)}
{tagsFacet && (
<TagsIcon className="little-spacer-right" />
{f.val}
</span>
- <span className="measure-value">{formatMeasure(f.count, 'SHORT_INT')}</span>
+ <span className="measure-value">
+ {formatMeasure(f.count, MetricType.ShortInteger)}
+ </span>
</div>
))}
</div>
cursor: initial;
}
-.issue.hotspot {
- background-color: var(--hotspotBgColor);
-}
-
.issue.selected,
.issue-message-box.selected {
box-shadow: none;
import { BranchLike } from '../../types/branch-like';
import { Issue as TypeIssue } from '../../types/types';
import { updateIssue } from './actions';
+import IssueView from './components/IssueView';
import './Issue.css';
-import IssueView from './IssueView';
interface Props {
branchLike?: BranchLike;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 classNames from 'classnames';
-import * as React from 'react';
-import { deleteIssueComment, editIssueComment } from '../../api/issues';
-import Checkbox from '../../components/controls/Checkbox';
-import { translate, translateWithParameters } from '../../helpers/l10n';
-import { BranchLike } from '../../types/branch-like';
-import { Issue } from '../../types/types';
-import { updateIssue } from './actions';
-import IssueActionsBar from './components/IssueActionsBar';
-import IssueCommentLine from './components/IssueCommentLine';
-import IssueTitleBar from './components/IssueTitleBar';
-
-interface Props {
- branchLike?: BranchLike;
- checked?: boolean;
- currentPopup?: string;
- displayWhyIsThisAnIssue?: boolean;
- displayLocationsCount?: boolean;
- displayLocationsLink?: boolean;
- issue: Issue;
- onAssign: (login: string) => void;
- onChange: (issue: Issue) => void;
- onCheck?: (issue: string) => void;
- onClick?: (issueKey: string) => void;
- onFilter?: (property: string, issue: Issue) => void;
- selected: boolean;
- togglePopup: (popup: string, show: boolean | void) => void;
-}
-
-export default class IssueView extends React.PureComponent<Props> {
- handleCheck = () => {
- if (this.props.onCheck) {
- this.props.onCheck(this.props.issue.key);
- }
- };
-
- handleBoxClick = (event: React.MouseEvent<HTMLDivElement>) => {
- if (!isClickable(event.target as HTMLElement) && this.props.onClick) {
- event.preventDefault();
- this.handleDetailClick();
- }
- };
-
- handleDetailClick = () => {
- if (this.props.onClick) {
- this.props.onClick(this.props.issue.key);
- }
- };
-
- editComment = (comment: string, text: string) => {
- updateIssue(this.props.onChange, editIssueComment({ comment, text }));
- };
-
- deleteComment = (comment: string) => {
- updateIssue(this.props.onChange, deleteIssueComment({ comment }));
- };
-
- render() {
- const {
- issue,
- branchLike,
- checked,
- currentPopup,
- displayWhyIsThisAnIssue,
- displayLocationsLink,
- displayLocationsCount,
- } = this.props;
-
- const hasCheckbox = this.props.onCheck != null;
-
- const issueClass = classNames('issue', {
- 'no-click': this.props.onClick === undefined,
- hotspot: issue.type === 'SECURITY_HOTSPOT',
- 'issue-with-checkbox': hasCheckbox,
- selected: this.props.selected,
- });
-
- return (
- <div
- className={issueClass}
- onClick={this.handleBoxClick}
- role="region"
- aria-label={issue.message}
- >
- {hasCheckbox && (
- <Checkbox
- checked={checked || false}
- className="issue-checkbox-container"
- onCheck={this.handleCheck}
- label={translateWithParameters('issues.action_select.label', issue.message)}
- title={translate('issues.action_select')}
- />
- )}
- <IssueTitleBar
- branchLike={branchLike}
- onClick={this.handleDetailClick}
- currentPopup={currentPopup}
- displayLocationsCount={displayLocationsCount}
- displayLocationsLink={displayLocationsLink}
- displayWhyIsThisAnIssue={displayWhyIsThisAnIssue}
- issue={issue}
- onFilter={this.props.onFilter}
- togglePopup={this.props.togglePopup}
- />
- <IssueActionsBar
- className="padded-left"
- currentPopup={currentPopup}
- issue={issue}
- onAssign={this.props.onAssign}
- onChange={this.props.onChange}
- togglePopup={this.props.togglePopup}
- />
- {issue.comments && issue.comments.length > 0 && (
- <div className="issue-comments">
- {issue.comments.map((comment) => (
- <IssueCommentLine
- comment={comment}
- key={comment.key}
- onDelete={this.deleteComment}
- onEdit={this.editComment}
- />
- ))}
- </div>
- )}
- </div>
- );
- }
-}
-
-function isClickable(node: HTMLElement | undefined | null): boolean {
- if (!node) {
- return false;
- }
- const clickableTags = ['A', 'BUTTON', 'INPUT', 'TEXTAREA'];
- const tagName = (node.tagName || '').toUpperCase();
- return clickableTags.includes(tagName) || isClickable(node.parentElement);
-}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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 { act, screen, within } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { omit, pick } from 'lodash';
+import * as React from 'react';
+import { byRole, byText } from 'testing-library-selector';
+import IssuesServiceMock from '../../../api/mocks/IssuesServiceMock';
+import { KeyboardKeys } from '../../../helpers/keycodes';
+import { mockIssueComment } from '../../../helpers/mocks/issues';
+import { mockIssue, mockLoggedInUser, mockRawIssue } from '../../../helpers/testMocks';
+import { findTooltipWithContent, renderApp } from '../../../helpers/testReactTestingUtils';
+import {
+ IssueActions,
+ IssueSeverity,
+ IssueStatus,
+ IssueTransition,
+ IssueType,
+} from '../../../types/issues';
+import { RuleStatus } from '../../../types/rules';
+import { IssueComment } from '../../../types/types';
+import Issue from '../Issue';
+
+jest.mock('../../../api/issues');
+jest.mock('../../../api/rules');
+jest.mock('../../../api/users');
+
+jest.mock('../../../helpers/preferences', () => ({
+ getKeyboardShortcutEnabled: jest.fn(() => true),
+}));
+
+const issuesHandler = new IssuesServiceMock();
+
+beforeEach(() => {
+ issuesHandler.reset();
+});
+
+describe('rendering', () => {
+ it('should render correctly with comments', () => {
+ const { ui } = getPageObject();
+ renderIssue({ issue: mockIssue(false, { comments: [mockIssueCommentPosted4YearsAgo()] }) });
+
+ const comments = within(ui.commentsList());
+ expect(comments.getByText('Leïa Skywalker')).toBeInTheDocument();
+ expect(comments.getByRole('listitem')).toHaveTextContent('This is a comment, bud');
+ expect(comments.getByRole('listitem')).toHaveTextContent('issue.comment.posted_on4 years ago');
+ });
+
+ it('should render correctly for locations, issue message, line, permalink, why, and effort', async () => {
+ const { ui } = getPageObject();
+ const issue = mockIssue(true, { effort: '2 days', message: 'This is an issue' });
+ const onClick = jest.fn();
+ renderIssue({ issue, displayLocationsCount: true, displayWhyIsThisAnIssue: true, onClick });
+
+ expect(ui.locationsBadge(7).get()).toBeInTheDocument();
+ expect(ui.lineInfo(26).get()).toBeInTheDocument();
+ expect(ui.permalink.get()).toHaveAttribute(
+ 'href',
+ `/project/issues?issues=${issue.key}&open=${issue.key}&id=${issue.project}`
+ );
+ expect(ui.whyLink.get()).toBeInTheDocument();
+ expect(ui.effort('2 days').get()).toBeInTheDocument();
+ await ui.clickIssueMessage();
+ expect(onClick).toHaveBeenCalledWith(issue.key);
+ });
+
+ it.each([RuleStatus.Deprecated, RuleStatus.Removed])(
+ 'should render correctly for a %s rule',
+ (ruleStatus) => {
+ const { ui } = getPageObject();
+ renderIssue({ issue: mockIssue(false, { ruleStatus }) });
+ expect(ui.ruleStatusBadge(ruleStatus).get()).toBeInTheDocument();
+ }
+ );
+
+ it('should render correctly for external rule engines', () => {
+ renderIssue({ issue: mockIssue(true, { externalRuleEngine: 'ESLINT' }) });
+ expect(screen.getByText('ESLINT')).toBeInTheDocument();
+ });
+
+ it('should render the SonarLint icon correctly', () => {
+ renderIssue({ issue: mockIssue(false, { quickFixAvailable: true }) });
+ expect(
+ findTooltipWithContent('issue.quick_fix_available_with_sonarlint_no_link')
+ ).toBeInTheDocument();
+ });
+
+ it('should render correctly with a checkbox', async () => {
+ const { ui } = getPageObject();
+ const onCheck = jest.fn();
+ const issue = mockIssue();
+ renderIssue({ onCheck, issue });
+ await ui.toggleCheckbox();
+ expect(onCheck).toHaveBeenCalledWith(issue.key);
+ });
+
+ it('should correctly render the changelog', async () => {
+ const { ui } = getPageObject();
+ renderIssue();
+
+ await ui.showChangelog();
+ expect(
+ ui.changelogRow('status', IssueStatus.Confirmed, IssueStatus.Reopened).get()
+ ).toBeInTheDocument();
+ expect(ui.changelogRow('assign', 'luke.skywalker', 'darth.vader').get()).toBeInTheDocument();
+ });
+});
+
+describe('updating', () => {
+ it('should allow updating the type', async () => {
+ const { ui } = getPageObject();
+ const issue = mockRawIssue(false, {
+ type: IssueType.Bug,
+ actions: [IssueActions.SetType],
+ });
+ issuesHandler.setIssueList([{ issue, snippets: {} }]);
+ renderIssue({ issue: mockIssue(false, { ...pick(issue, 'actions', 'key', 'type') }) });
+
+ await ui.updateType(IssueType.Bug, IssueType.CodeSmell);
+ expect(ui.updateTypeBtn(IssueType.CodeSmell).get()).toBeInTheDocument();
+ });
+
+ it('should allow updating the severity', async () => {
+ const { ui } = getPageObject();
+ const issue = mockRawIssue(false, {
+ severity: IssueSeverity.Blocker,
+ actions: [IssueActions.SetSeverity],
+ });
+ issuesHandler.setIssueList([{ issue, snippets: {} }]);
+ renderIssue({ issue: mockIssue(false, { ...pick(issue, 'actions', 'key', 'severity') }) });
+
+ await ui.updateSeverity(IssueSeverity.Blocker, IssueSeverity.Minor);
+ expect(ui.updateSeverityBtn(IssueSeverity.Minor).get()).toBeInTheDocument();
+ });
+
+ it('should allow updating the status', async () => {
+ const { ui } = getPageObject();
+ const issue = mockRawIssue(false, {
+ status: IssueStatus.Open,
+ transitions: [IssueTransition.Confirm, IssueTransition.UnConfirm],
+ });
+ issuesHandler.setIssueList([{ issue, snippets: {} }]);
+ renderIssue({
+ issue: mockIssue(false, { ...pick(issue, 'key', 'status', 'transitions') }),
+ });
+
+ await ui.updateStatus(IssueStatus.Open, IssueTransition.Confirm);
+ expect(ui.updateStatusBtn(IssueStatus.Confirmed).get()).toBeInTheDocument();
+ });
+
+ it('should allow assigning', async () => {
+ const { ui } = getPageObject();
+ const issue = mockRawIssue(false, {
+ assignee: 'leia',
+ actions: [IssueActions.Assign],
+ });
+ issuesHandler.setIssueList([{ issue, snippets: {} }]);
+ renderIssue({
+ issue: mockIssue(false, { ...pick(issue, 'actions', 'key', 'assignee') }),
+ });
+
+ await ui.updateAssignee('leia', 'Skywalker');
+ expect(ui.updateAssigneeBtn('luke').get()).toBeInTheDocument();
+ });
+
+ it('should allow commenting', async () => {
+ const { ui } = getPageObject();
+ const issue = mockRawIssue(false, {
+ actions: [IssueActions.Comment],
+ });
+ issuesHandler.setIssueList([{ issue, snippets: {} }]);
+ renderIssue({ issue: mockIssue(false, { ...pick(issue, 'actions', 'key') }) });
+
+ // Create
+ await ui.addComment('Original content');
+ const comments = within(ui.commentsList());
+ expect(comments.getByRole('listitem')).toHaveTextContent('Original content');
+
+ // Update
+ await ui.updateComment('New content');
+ expect(comments.getByRole('listitem')).toHaveTextContent('New content');
+
+ // Delete
+ await ui.deleteComment();
+ expect(comments.getByRole('listitem')).toHaveTextContent('New content');
+ });
+
+ it('should allow updating the tags', async () => {
+ const { ui } = getPageObject();
+ const issue = mockRawIssue(false, {
+ tags: [],
+ actions: [IssueActions.SetTags],
+ });
+ issuesHandler.setIssueList([{ issue, snippets: {} }]);
+ renderIssue({ issue: mockIssue(false, { ...pick(issue, 'actions', 'key', 'tags') }) });
+
+ await ui.addTag('accessibility');
+ await ui.addTag('android', ['accessibility']);
+ expect(ui.updateTagsBtn(['accessibility', 'android']).get()).toBeInTheDocument();
+ });
+});
+
+it('should correctly handle keyboard shortcuts', async () => {
+ const { ui } = getPageObject();
+ const onCheck = jest.fn();
+ const issue = mockRawIssue(false, {
+ actions: Object.values(IssueActions),
+ assignee: 'luke',
+ transitions: [IssueTransition.Confirm, IssueTransition.UnConfirm],
+ });
+ issuesHandler.setIssueList([{ issue, snippets: {} }]);
+ issuesHandler.setCurrentUser(mockLoggedInUser({ login: 'leia', name: 'Organa' }));
+ renderIssue({
+ onCheck,
+ selected: true,
+ issue: mockIssue(false, { ...pick(issue, 'actions', 'key', 'assignee', 'transitions') }),
+ });
+
+ await ui.pressTransitionShortcut();
+ expect(ui.setStatusBtn(IssueTransition.UnConfirm).get()).toBeInTheDocument();
+ await ui.pressDismissShortcut();
+
+ await ui.pressAssignShortcut();
+ expect(ui.setAssigneeBtn(/Organa/).get()).toBeInTheDocument();
+ await ui.pressDismissShortcut();
+
+ await ui.pressSeverityShortcut();
+ expect(ui.setSeverityBtn(IssueSeverity.Minor).get()).toBeInTheDocument();
+ await ui.pressDismissShortcut();
+
+ await ui.pressCommentShortcut();
+ expect(ui.commentTextInput.get()).toBeInTheDocument();
+ await ui.pressDismissShortcut();
+
+ await ui.pressTagsShortcut();
+ expect(ui.tagsSearchInput.get()).toBeInTheDocument();
+ await ui.pressDismissShortcut();
+
+ await ui.pressCheckShortcut();
+ expect(onCheck).toHaveBeenCalled();
+
+ expect(ui.updateAssigneeBtn('luke').get()).toBeInTheDocument();
+ await ui.pressAssignToMeShortcut();
+ expect(ui.updateAssigneeBtn('leia').get()).toBeInTheDocument();
+});
+
+it('should correctly handle similar issues filtering', async () => {
+ const { ui, user } = getPageObject();
+ const onFilter = jest.fn();
+ const issue = mockIssue(false, {
+ ruleName: 'Rule Foo',
+ tags: ['accessibility', 'owasp'],
+ projectName: 'Project Bar',
+ componentLongName: 'main.js',
+ });
+ renderIssue({ onFilter, issue });
+
+ await ui.showSimilarIssues();
+ await user.click(ui.similarIssueTypeLink.get());
+ expect(onFilter).toHaveBeenLastCalledWith('type', issue);
+
+ await ui.showSimilarIssues();
+ await user.click(ui.similarIssueSeverityLink.get());
+ expect(onFilter).toHaveBeenLastCalledWith('severity', issue);
+
+ await ui.showSimilarIssues();
+ await user.click(ui.similarIssueStatusLink.get());
+ expect(onFilter).toHaveBeenLastCalledWith('status', issue);
+
+ await ui.showSimilarIssues();
+ await user.click(ui.similarIssueResolutionLink.get());
+ expect(onFilter).toHaveBeenLastCalledWith('resolution', issue);
+
+ await ui.showSimilarIssues();
+ await user.click(ui.similarIssueAssigneeLink.get());
+ expect(onFilter).toHaveBeenLastCalledWith('assignee', issue);
+
+ await ui.showSimilarIssues();
+ await user.click(ui.similarIssueRuleLink.get());
+ expect(onFilter).toHaveBeenLastCalledWith('rule', issue);
+
+ await ui.showSimilarIssues();
+ await user.click(ui.similarIssueTagLink('accessibility').get());
+ expect(onFilter).toHaveBeenLastCalledWith('tag###accessibility', issue);
+
+ await ui.showSimilarIssues();
+ await user.click(ui.similarIssueTagLink('owasp').get());
+ expect(onFilter).toHaveBeenLastCalledWith('tag###owasp', issue);
+
+ await ui.showSimilarIssues();
+ await user.click(ui.similarIssueProjectLink.get());
+ expect(onFilter).toHaveBeenLastCalledWith('project', issue);
+
+ await ui.showSimilarIssues();
+ await user.click(ui.similarIssueFileLink.get());
+ expect(onFilter).toHaveBeenLastCalledWith('file', issue);
+});
+
+function mockIssueCommentPosted4YearsAgo(overrides: Partial<IssueComment> = {}) {
+ const date = new Date();
+ date.setFullYear(date.getFullYear() - 4);
+ return mockIssueComment({
+ authorName: 'Leïa Skywalker',
+ createdAt: date.toISOString(),
+ ...overrides,
+ });
+}
+
+function getPageObject() {
+ const user = userEvent.setup();
+
+ const selectors = {
+ // Issue
+ ruleStatusBadge: (status: RuleStatus) => byText(`issue.resolution.badge.${status}`),
+ locationsBadge: (count: number) => byText(count),
+ lineInfo: (line: number) => byText(`L${line}`),
+ permalink: byRole('link', { name: 'permalink' }),
+ effort: (effort: string) => byText(`issue.x_effort.${effort}`),
+ whyLink: byRole('link', { name: 'issue.why_this_issue.long' }),
+ checkbox: byRole('checkbox'),
+ issueMessageBtn: byRole('button', { name: 'This is an issue' }),
+
+ // Changelog
+ toggleChangelogBtn: byRole('button', {
+ name: /issue.changelog.found_on_x_show_more/,
+ }),
+ changelogRow: (key: string, oldValue: string, newValue: string) =>
+ byRole('row', {
+ name: new RegExp(
+ `issue\\.changelog\\.changed_to\\.issue\\.changelog\\.field\\.${key}\\.${newValue} \\(issue\\.changelog\\.was\\.${oldValue}\\)`
+ ),
+ }),
+
+ // Similar issues
+ toggleSimilarIssuesBtn: byRole('button', { name: 'issue.filter_similar_issues' }),
+ similarIssueTypeLink: byRole('button', { name: 'issue.type.BUG' }),
+ similarIssueSeverityLink: byRole('button', { name: 'severity.MAJOR' }),
+ similarIssueStatusLink: byRole('button', { name: 'issue.status.OPEN' }),
+ similarIssueResolutionLink: byRole('button', { name: 'unresolved' }),
+ similarIssueAssigneeLink: byRole('button', { name: 'unassigned' }),
+ similarIssueRuleLink: byRole('button', { name: 'Rule Foo' }),
+ similarIssueTagLink: (name: string) => byRole('button', { name }),
+ similarIssueProjectLink: byRole('button', { name: 'qualifier.TRK Project Bar' }),
+ similarIssueFileLink: byRole('button', { name: 'qualifier.FIL main.js' }),
+
+ // Comment
+ commentsList: () => {
+ const list = byRole('list')
+ .getAll()
+ .find((el) => el.getAttribute('data-testid') === 'issue-comments');
+ if (list === undefined) {
+ throw new Error('Could not find comments list');
+ }
+ return list;
+ },
+ commentAddBtn: byRole('button', { name: 'issue.comment.add_comment' }),
+ commentEditBtn: byRole('button', { name: 'issue.comment.edit' }),
+ commentTextInput: byRole('textbox', { name: 'issue.comment.enter_comment' }),
+ commentSaveBtn: byRole('button', { name: 'issue.comment.formlink' }),
+ commentUpdateBtn: byRole('button', { name: 'save' }),
+ commentDeleteBtn: byRole('button', { name: 'issue.comment.delete' }),
+ commentConfirmDeleteBtn: byRole('button', { name: 'delete' }),
+
+ // Type
+ updateTypeBtn: (currentType: IssueType) =>
+ byRole('button', { name: `issue.type.type_x_click_to_change.issue.type.${currentType}` }),
+ setTypeBtn: (type: IssueType) => byRole('button', { name: `issue.type.${type}` }),
+
+ // Severity
+ updateSeverityBtn: (currentSeverity: IssueSeverity) =>
+ byRole('button', {
+ name: `issue.severity.severity_x_click_to_change.severity.${currentSeverity}`,
+ }),
+ setSeverityBtn: (severity: IssueSeverity) => byRole('button', { name: `severity.${severity}` }),
+
+ // Status
+ updateStatusBtn: (currentStatus: IssueStatus) =>
+ byRole('button', {
+ name: `issue.transition.status_x_click_to_change.issue.status.${currentStatus}`,
+ }),
+ setStatusBtn: (transition: IssueTransition) =>
+ byRole('button', { name: `issue.transition.${transition}` }),
+
+ // Assignee
+ assigneeSearchInput: byRole('searchbox'),
+ updateAssigneeBtn: (currentAssignee: string) =>
+ byRole('button', {
+ name: `issue.assign.assigned_to_x_click_to_change.${currentAssignee}`,
+ }),
+ setAssigneeBtn: (name: RegExp) => byRole('button', { name }),
+
+ // Tags
+ tagsSearchInput: byRole('searchbox'),
+ updateTagsBtn: (currentTags?: string[]) =>
+ byRole('button', {
+ name: `tags_list_x.${currentTags ? currentTags.join(', ') : 'issue.no_tag'}`,
+ }),
+ toggleTagCheckbox: (name: string) => byRole('checkbox', { name }),
+ };
+
+ const ui = {
+ ...selectors,
+ async addComment(content: string) {
+ await user.click(selectors.commentAddBtn.get());
+ await user.type(selectors.commentTextInput.get(), content);
+ await act(async () => {
+ await user.click(selectors.commentSaveBtn.get());
+ });
+ },
+ async updateComment(content: string) {
+ await user.click(selectors.commentEditBtn.get());
+ await user.type(selectors.commentTextInput.get(), content);
+ await act(async () => {
+ await user.keyboard(`{Control>}{${KeyboardKeys.Enter}}{/Control}`);
+ });
+ },
+ async deleteComment() {
+ await user.click(selectors.commentDeleteBtn.get());
+ await act(async () => {
+ await user.click(selectors.commentConfirmDeleteBtn.get());
+ });
+ },
+ async updateType(currentType: IssueType, newType: IssueType) {
+ await user.click(selectors.updateTypeBtn(currentType).get());
+ await act(async () => {
+ await user.click(selectors.setTypeBtn(newType).get());
+ });
+ },
+ async updateSeverity(currentSeverity: IssueSeverity, newSeverity: IssueSeverity) {
+ await user.click(selectors.updateSeverityBtn(currentSeverity).get());
+ await act(async () => {
+ await user.click(selectors.setSeverityBtn(newSeverity).get());
+ });
+ },
+ async updateStatus(currentStatus: IssueStatus, transition: IssueTransition) {
+ await user.click(selectors.updateStatusBtn(currentStatus).get());
+ await act(async () => {
+ await user.click(selectors.setStatusBtn(transition).get());
+ });
+ },
+ async updateAssignee(currentAssignee: string, newAssignee: string) {
+ await user.click(selectors.updateAssigneeBtn(currentAssignee).get());
+ await user.type(selectors.assigneeSearchInput.get(), newAssignee);
+ await act(async () => {
+ await user.click(selectors.setAssigneeBtn(new RegExp(newAssignee)).get());
+ });
+ },
+ async addTag(tag: string, currentTagList?: string[]) {
+ await user.click(selectors.updateTagsBtn(currentTagList).get());
+ await act(async () => {
+ await user.click(selectors.toggleTagCheckbox(tag).get());
+ });
+ await act(async () => {
+ await user.keyboard('{Escape}');
+ });
+ },
+ async showChangelog() {
+ await user.click(selectors.toggleChangelogBtn.get());
+ },
+ async showSimilarIssues() {
+ await user.click(selectors.toggleSimilarIssuesBtn.get());
+ },
+ async toggleCheckbox() {
+ await user.click(selectors.checkbox.get());
+ },
+ async clickIssueMessage() {
+ await user.click(selectors.issueMessageBtn.get());
+ },
+ async pressDismissShortcut() {
+ await act(async () => {
+ await user.keyboard(`{${KeyboardKeys.Escape}}`);
+ });
+ },
+ async pressTransitionShortcut() {
+ await act(async () => {
+ await user.keyboard(`{${KeyboardKeys.KeyF}}`);
+ });
+ },
+ async pressAssignShortcut() {
+ await act(async () => {
+ await user.keyboard(`{${KeyboardKeys.KeyA}}`);
+ });
+ },
+ async pressAssignToMeShortcut() {
+ await act(async () => {
+ await user.keyboard(`{${KeyboardKeys.KeyM}}`);
+ });
+ },
+ async pressSeverityShortcut() {
+ await act(async () => {
+ await user.keyboard(`{${KeyboardKeys.KeyI}}`);
+ });
+ },
+ async pressCommentShortcut() {
+ await act(async () => {
+ await user.keyboard(`{${KeyboardKeys.KeyC}}`);
+ });
+ },
+ async pressTagsShortcut() {
+ await act(async () => {
+ await user.keyboard(`{${KeyboardKeys.KeyT}}`);
+ });
+ },
+ async pressCheckShortcut() {
+ await act(async () => {
+ await user.keyboard(`{${KeyboardKeys.Space}}`);
+ });
+ },
+ };
+
+ return { ui, user };
+}
+
+function renderIssue(props: Partial<Omit<Issue['props'], 'onChange' | 'onPopupToggle'>> = {}) {
+ function Wrapper(wrapperProps: Omit<Issue['props'], 'onChange' | 'onPopupToggle'>) {
+ const [issue, setIssue] = React.useState(wrapperProps.issue);
+ const [openPopup, setOpenPopup] = React.useState<string | undefined>();
+ return (
+ <Issue
+ issue={issue}
+ openPopup={openPopup}
+ onChange={(newIssue) => {
+ setIssue({ ...issue, ...newIssue });
+ }}
+ onPopupToggle={(_key, popup, open) => {
+ setOpenPopup(open === false ? undefined : popup);
+ }}
+ {...omit(wrapperProps, 'issue')}
+ />
+ );
+ }
+
+ return renderApp('/', <Wrapper issue={mockIssue()} selected={false} {...props} />, {
+ currentUser: mockLoggedInUser({ login: 'leia', name: 'Organa' }),
+ });
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 * as React from 'react';
-import { mockIssue } from '../../../helpers/testMocks';
-import IssueView from '../IssueView';
-
-it('should render issues correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should render hotspots correctly', () => {
- expect(
- shallowRender({ issue: mockIssue(false, { type: 'SECURITY_HOTSPOT' }) })
- ).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<IssueView['props']> = {}) {
- return shallow(
- <IssueView
- issue={mockIssue(false, {
- comments: [
- {
- key: '1',
- htmlText: 'My comment',
- markdown: 'My comment',
- updatable: false,
- createdAt: '2017-07-05T09:33:29+0200',
- author: 'admin',
- authorLogin: 'admin',
- authorName: 'Admin',
- authorAvatar: 'admin-avatar',
- authorActive: true,
- },
- ],
- })}
- onAssign={jest.fn()}
- onChange={jest.fn()}
- onClick={jest.fn()}
- selected={true}
- togglePopup={jest.fn()}
- {...props}
- />
- );
-}
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render hotspots correctly 1`] = `
-<div
- aria-label="Reduce the number of conditional operators (4) used in the expression"
- className="issue hotspot selected"
- onClick={[Function]}
- role="region"
->
- <IssueTitleBar
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "SECURITY_HOTSPOT",
- }
- }
- onClick={[Function]}
- togglePopup={[MockFunction]}
- />
- <IssueActionsBar
- className="padded-left"
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "SECURITY_HOTSPOT",
- }
- }
- onAssign={[MockFunction]}
- onChange={[MockFunction]}
- togglePopup={[MockFunction]}
- />
-</div>
-`;
-
-exports[`should render issues correctly 1`] = `
-<div
- aria-label="Reduce the number of conditional operators (4) used in the expression"
- className="issue selected"
- onClick={[Function]}
- role="region"
->
- <IssueTitleBar
- issue={
- {
- "actions": [],
- "comments": [
- {
- "author": "admin",
- "authorActive": true,
- "authorAvatar": "admin-avatar",
- "authorLogin": "admin",
- "authorName": "Admin",
- "createdAt": "2017-07-05T09:33:29+0200",
- "htmlText": "My comment",
- "key": "1",
- "markdown": "My comment",
- "updatable": false,
- },
- ],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- onClick={[Function]}
- togglePopup={[MockFunction]}
- />
- <IssueActionsBar
- className="padded-left"
- issue={
- {
- "actions": [],
- "comments": [
- {
- "author": "admin",
- "authorActive": true,
- "authorAvatar": "admin-avatar",
- "authorLogin": "admin",
- "authorName": "Admin",
- "createdAt": "2017-07-05T09:33:29+0200",
- "htmlText": "My comment",
- "key": "1",
- "markdown": "My comment",
- "updatable": false,
- },
- ],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- onAssign={[MockFunction]}
- onChange={[MockFunction]}
- togglePopup={[MockFunction]}
- />
- <div
- className="issue-comments"
- >
- <IssueCommentLine
- comment={
- {
- "author": "admin",
- "authorActive": true,
- "authorAvatar": "admin-avatar",
- "authorLogin": "admin",
- "authorName": "Admin",
- "createdAt": "2017-07-05T09:33:29+0200",
- "htmlText": "My comment",
- "key": "1",
- "markdown": "My comment",
- "updatable": false,
- }
- }
- key="1"
- onDelete={[Function]}
- onEdit={[Function]}
- />
- </div>
-</div>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render issues correctly 1`] = `
-<IssueView
- displayLocationsCount={true}
- displayLocationsLink={false}
- issue={
- {
- "actions": [],
- "comments": [
- {
- "author": "admin",
- "authorActive": true,
- "authorAvatar": "admin-avatar",
- "authorLogin": "admin",
- "authorName": "Admin",
- "createdAt": "2017-07-05T09:33:29+0200",
- "htmlText": "My comment",
- "key": "1",
- "markdown": "My comment",
- "updatable": false,
- },
- ],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- onAssign={[Function]}
- onChange={[MockFunction]}
- onCheck={[MockFunction]}
- onClick={[MockFunction]}
- onFilter={[MockFunction]}
- selected={true}
- togglePopup={[Function]}
-/>
-`;
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 * as React from 'react';
-import { KeyboardKeys } from '../../../helpers/keycodes';
-import { mockIssue } from '../../../helpers/testMocks';
-import { keydown } from '../../../helpers/testUtils';
-import Issue from '../Issue';
-
-it('should render issues correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should call the proper function with the proper props when pressing shortcuts (FAMICT)', () => {
- const onPopupToggle = jest.fn();
- const onCheck = jest.fn();
- const issue = mockIssue(false, {
- comments: [
- {
- key: '1',
- htmlText: 'My comment',
- markdown: 'My comment',
- updatable: false,
- createdAt: '2017-07-05T09:33:29+0200',
- author: 'admin',
- authorLogin: 'admin',
- authorName: 'Admin',
- authorAvatar: 'admin-avatar',
- authorActive: true,
- },
- ],
- actions: ['assign'],
- });
-
- shallowRender({ onPopupToggle, issue, onCheck });
- keydown({ key: KeyboardKeys.KeyF, metaKey: true });
- expect(onPopupToggle).not.toHaveBeenCalledWith(issue.key, 'transition', undefined);
-
- keydown({ key: KeyboardKeys.KeyF });
- expect(onPopupToggle).toHaveBeenCalledWith(issue.key, 'transition', undefined);
-
- keydown({ key: KeyboardKeys.KeyA });
- expect(onPopupToggle).toHaveBeenCalledWith(issue.key, 'assign', undefined);
- keydown({ key: KeyboardKeys.Escape });
-
- keydown({ key: KeyboardKeys.KeyM });
- expect(onPopupToggle).toHaveBeenCalledWith(issue.key, 'assign', false);
-
- keydown({ key: KeyboardKeys.KeyI });
- expect(onPopupToggle).toHaveBeenCalledWith(issue.key, 'set-severity', undefined);
-
- keydown({ key: KeyboardKeys.KeyC, metaKey: true });
- expect(onPopupToggle).not.toHaveBeenCalledWith(issue.key, 'comment', undefined);
-
- keydown({ key: KeyboardKeys.KeyC });
- expect(onPopupToggle).toHaveBeenCalledWith(issue.key, 'comment', undefined);
- keydown({ key: KeyboardKeys.Escape });
-
- keydown({ key: KeyboardKeys.KeyT });
- expect(onPopupToggle).toHaveBeenCalledWith(issue.key, 'edit-tags', undefined);
-
- keydown({ key: KeyboardKeys.Space });
- expect(onCheck).toHaveBeenCalledWith(issue.key);
-});
-
-function shallowRender(props: Partial<Issue['props']> = {}) {
- return shallow<Issue>(
- <Issue
- displayLocationsCount={true}
- displayLocationsLink={false}
- issue={mockIssue(false, {
- comments: [
- {
- key: '1',
- htmlText: 'My comment',
- markdown: 'My comment',
- updatable: false,
- createdAt: '2017-07-05T09:33:29+0200',
- author: 'admin',
- authorLogin: 'admin',
- authorName: 'Admin',
- authorAvatar: 'admin-avatar',
- authorActive: true,
- },
- ],
- })}
- onChange={jest.fn()}
- onCheck={jest.fn()}
- onClick={jest.fn()}
- onFilter={jest.fn()}
- onPopupToggle={jest.fn()}
- selected={true}
- {...props}
- />
- );
-}
import classNames from 'classnames';
import * as React from 'react';
import { translate, translateWithParameters } from '../../../helpers/l10n';
-import { IssueResolution, IssueResponse, IssueType as IssueTypeEnum } from '../../../types/issues';
+import {
+ IssueActions,
+ IssueResolution,
+ IssueResponse,
+ IssueType as IssueTypeEnum,
+} from '../../../types/issues';
import { Issue, RawQuery } from '../../../types/types';
import { updateIssue } from '../actions';
import IssueAssign from './IssueAssign';
render() {
const { issue, className, showCommentsInPopup } = this.props;
- const canAssign = issue.actions.includes('assign');
- const canComment = issue.actions.includes('comment');
- const canSetSeverity = issue.actions.includes('set_severity');
- const canSetType = issue.actions.includes('set_type');
- const canSetTags = issue.actions.includes('set_tags');
- const hasTransitions = issue.transitions && issue.transitions.length > 0;
- const isSecurityHotspot = issue.type === IssueTypeEnum.SecurityHotspot;
+ const canAssign = issue.actions.includes(IssueActions.Assign);
+ const canComment = issue.actions.includes(IssueActions.Comment);
+ const canSetSeverity = issue.actions.includes(IssueActions.SetSeverity);
+ const canSetType = issue.actions.includes(IssueActions.SetType);
+ const canSetTags = issue.actions.includes(IssueActions.SetTags);
+ const hasTransitions = issue.transitions.length > 0;
return (
<div className={classNames(className, 'issue-actions')}>
togglePopup={this.props.togglePopup}
/>
</div>
- {!isSecurityHotspot && (
- <div className="issue-meta">
- <IssueSeverity
- canSetSeverity={canSetSeverity}
- isOpen={this.props.currentPopup === 'set-severity' && canSetSeverity}
- issue={issue}
- setIssueProperty={this.setIssueProperty}
- togglePopup={this.props.togglePopup}
- />
- </div>
- )}
+ <div className="issue-meta">
+ <IssueSeverity
+ canSetSeverity={canSetSeverity}
+ isOpen={this.props.currentPopup === 'set-severity' && canSetSeverity}
+ issue={issue}
+ setIssueProperty={this.setIssueProperty}
+ togglePopup={this.props.togglePopup}
+ />
+ </div>
<div className="issue-meta">
<IssueTransition
hasTransitions={hasTransitions}
togglePopup={this.props.togglePopup}
/>
</div>
- {!isSecurityHotspot && issue.effort && (
+ {issue.effort && (
<div className="issue-meta">
<span className="issue-meta-label">
{translateWithParameters('issue.x_effort', issue.effort)}
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
+import { injectIntl, WrappedComponentProps } from 'react-intl';
import { ButtonLink } from '../../../components/controls/buttons';
import Toggler from '../../../components/controls/Toggler';
import DropdownIcon from '../../../components/icons/DropdownIcon';
+import { translateWithParameters } from '../../../helpers/l10n';
import { Issue } from '../../../types/types';
+import { formatterOption } from '../../intl/DateFormatter';
import DateFromNow from '../../intl/DateFromNow';
import ChangelogPopup from '../popups/ChangelogPopup';
-interface Props {
+export interface IssueChangelogProps extends WrappedComponentProps {
isOpen: boolean;
issue: Pick<Issue, 'author' | 'creationDate' | 'key'>;
creationDate: string;
togglePopup: (popup: string, show?: boolean) => void;
}
-export default class IssueChangelog extends React.PureComponent<Props> {
- toggleChangelog = (open?: boolean) => {
- this.props.togglePopup('changelog', open);
- };
-
- handleClick = () => {
- this.toggleChangelog();
- };
-
- handleClose = () => {
- this.toggleChangelog(false);
- };
-
- render() {
- return (
- <div className="dropdown">
- <Toggler
- onRequestClose={this.handleClose}
- open={this.props.isOpen}
- overlay={<ChangelogPopup issue={this.props.issue} />}
+function IssueChangelog(props: IssueChangelogProps) {
+ const {
+ isOpen,
+ issue,
+ creationDate,
+ intl: { formatDate },
+ } = props;
+ return (
+ <div className="dropdown">
+ <Toggler
+ onRequestClose={() => {
+ props.togglePopup('changelog', false);
+ }}
+ open={isOpen}
+ overlay={<ChangelogPopup issue={issue} />}
+ >
+ <ButtonLink
+ aria-expanded={isOpen}
+ aria-label={translateWithParameters(
+ 'issue.changelog.found_on_x_show_more',
+ formatDate(creationDate, formatterOption)
+ )}
+ className="issue-action issue-action-with-options js-issue-show-changelog"
+ onClick={() => {
+ props.togglePopup('changelog');
+ }}
>
- <ButtonLink
- aria-expanded={this.props.isOpen}
- className="issue-action issue-action-with-options js-issue-show-changelog"
- onClick={this.handleClick}
- >
- <span className="issue-meta-label">
- <DateFromNow date={this.props.creationDate} />
- </span>
- <DropdownIcon className="little-spacer-left" />
- </ButtonLink>
- </Toggler>
- </div>
- );
- }
+ <span className="issue-meta-label">
+ <DateFromNow date={creationDate} />
+ </span>
+ <DropdownIcon className="little-spacer-left" />
+ </ButtonLink>
+ </Toggler>
+ </div>
+ );
}
+
+export default injectIntl(IssueChangelog);
import { formatMeasure } from '../../../helpers/measures';
import { IssueChangelogDiff as TypeIssueChangelogDiff } from '../../../types/types';
-interface Props {
+export interface IssueChangelogDiffProps {
diff: TypeIssueChangelogDiff;
}
-export default function IssueChangelogDiff({ diff }: Props) {
+export default function IssueChangelogDiff({ diff }: IssueChangelogDiffProps) {
if (diff.key === 'file') {
return (
<p>
{translateWithParameters(
'issue.change.file_move',
- diff.oldValue || '',
- diff.newValue || ''
+ diff.oldValue ?? '',
+ diff.newValue ?? ''
)}
</p>
);
<p>
{translateWithParameters(
'issue.change.from_branch',
- diff.oldValue || '',
- diff.newValue || ''
+ diff.oldValue ?? '',
+ diff.newValue ?? ''
)}
</p>
);
<p>
{translateWithParameters(
'issue.change.from_non_branch',
- diff.oldValue || '',
- diff.newValue || ''
+ diff.oldValue ?? '',
+ diff.newValue ?? ''
)}
</p>
);
} else if (diff.key === 'line') {
- return <p>{translateWithParameters('issue.changelog.line_removed_X', diff.oldValue || '')}</p>;
+ return <p>{translateWithParameters('issue.changelog.line_removed_X', diff.oldValue ?? '')}</p>;
}
let message;
? translateWithParameters('user.x_deleted', author)
: author;
return (
- <div className="issue-comment">
+ <li className="issue-comment">
<div className="issue-comment-author" title={displayName}>
<Avatar
className="little-spacer-right"
</div>
)}
</div>
- </div>
+ </li>
);
}
}
const { engine, quickFixAvailable, ruleStatus } = props;
const { externalRulesRepoNames } = React.useContext(WorkspaceContext);
- const ruleEngine = (engine && externalRulesRepoNames && externalRulesRepoNames[engine]) || engine;
+ const ruleEngine = (engine && externalRulesRepoNames[engine]) || engine;
return (
<>
<SonarLintIcon
className="it__issues-sonarlint-quick-fix spacer-right"
size={15}
- label="sonar-lint-icon"
+ description={translate('issue.quick_fix_available_with_sonarlint_no_link')}
/>
</Tooltip>
)}
import { formatMeasure } from '../../../helpers/measures';
import { getComponentIssuesUrl } from '../../../helpers/urls';
import { BranchLike } from '../../../types/branch-like';
+import { IssueType } from '../../../types/issues';
+import { MetricType } from '../../../types/metrics';
import { Issue } from '../../../types/types';
import LocationIndex from '../../common/LocationIndex';
import IssueChangelog from './IssueChangelog';
<Tooltip
overlay={translateWithParameters(
'issue.this_issue_involves_x_code_locations',
- formatMeasure(locationsCount, 'INT')
+ formatMeasure(locationsCount, MetricType.Integer)
)}
>
<LocationIndex>{locationsCount}</LocationIndex>
...getBranchLikeQuery(props.branchLike),
issues: issue.key,
open: issue.key,
- types: issue.type === 'SECURITY_HOTSPOT' ? issue.type : undefined,
+ types: issue.type === IssueType.SecurityHotspot ? issue.type : undefined,
});
return (
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2023 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 classNames from 'classnames';
+import * as React from 'react';
+import { deleteIssueComment, editIssueComment } from '../../../api/issues';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
+import { BranchLike } from '../../../types/branch-like';
+import { Issue } from '../../../types/types';
+import Checkbox from '../../controls/Checkbox';
+import { updateIssue } from '../actions';
+import IssueActionsBar from './IssueActionsBar';
+import IssueCommentLine from './IssueCommentLine';
+import IssueTitleBar from './IssueTitleBar';
+
+interface Props {
+ branchLike?: BranchLike;
+ checked?: boolean;
+ currentPopup?: string;
+ displayWhyIsThisAnIssue?: boolean;
+ displayLocationsCount?: boolean;
+ displayLocationsLink?: boolean;
+ issue: Issue;
+ onAssign: (login: string) => void;
+ onChange: (issue: Issue) => void;
+ onCheck?: (issue: string) => void;
+ onClick?: (issueKey: string) => void;
+ onFilter?: (property: string, issue: Issue) => void;
+ selected: boolean;
+ togglePopup: (popup: string, show: boolean | void) => void;
+}
+
+export default class IssueView extends React.PureComponent<Props> {
+ handleCheck = () => {
+ if (this.props.onCheck) {
+ this.props.onCheck(this.props.issue.key);
+ }
+ };
+
+ handleBoxClick = (event: React.MouseEvent<HTMLDivElement>) => {
+ if (!isClickable(event.target as HTMLElement) && this.props.onClick) {
+ event.preventDefault();
+ this.handleDetailClick();
+ }
+ };
+
+ handleDetailClick = () => {
+ if (this.props.onClick) {
+ this.props.onClick(this.props.issue.key);
+ }
+ };
+
+ editComment = (comment: string, text: string) => {
+ updateIssue(this.props.onChange, editIssueComment({ comment, text }));
+ };
+
+ deleteComment = (comment: string) => {
+ updateIssue(this.props.onChange, deleteIssueComment({ comment }));
+ };
+
+ render() {
+ const {
+ issue,
+ branchLike,
+ checked,
+ currentPopup,
+ displayWhyIsThisAnIssue,
+ displayLocationsLink,
+ displayLocationsCount,
+ } = this.props;
+
+ const hasCheckbox = this.props.onCheck != null;
+
+ const issueClass = classNames('issue', {
+ 'no-click': this.props.onClick === undefined,
+ 'issue-with-checkbox': hasCheckbox,
+ selected: this.props.selected,
+ });
+
+ return (
+ <div
+ className={issueClass}
+ onClick={this.handleBoxClick}
+ role="region"
+ aria-label={issue.message}
+ >
+ {hasCheckbox && (
+ <Checkbox
+ checked={checked ?? false}
+ className="issue-checkbox-container"
+ onCheck={this.handleCheck}
+ label={translateWithParameters('issues.action_select.label', issue.message)}
+ title={translate('issues.action_select')}
+ />
+ )}
+ <IssueTitleBar
+ branchLike={branchLike}
+ onClick={this.handleDetailClick}
+ currentPopup={currentPopup}
+ displayLocationsCount={displayLocationsCount}
+ displayLocationsLink={displayLocationsLink}
+ displayWhyIsThisAnIssue={displayWhyIsThisAnIssue}
+ issue={issue}
+ onFilter={this.props.onFilter}
+ togglePopup={this.props.togglePopup}
+ />
+ <IssueActionsBar
+ className="padded-left"
+ currentPopup={currentPopup}
+ issue={issue}
+ onAssign={this.props.onAssign}
+ onChange={this.props.onChange}
+ togglePopup={this.props.togglePopup}
+ />
+ {issue.comments && issue.comments.length > 0 && (
+ <ul className="issue-comments" data-testid="issue-comments">
+ {issue.comments.map((comment) => (
+ <IssueCommentLine
+ comment={comment}
+ key={comment.key}
+ onDelete={this.deleteComment}
+ onEdit={this.editComment}
+ />
+ ))}
+ </ul>
+ )}
+ </div>
+ );
+ }
+}
+
+function isClickable(node: HTMLElement | undefined | null): boolean {
+ if (!node) {
+ return false;
+ }
+ const clickableTags = ['A', 'BUTTON', 'INPUT', 'TEXTAREA'];
+ const tagName = (node.tagName || '').toUpperCase();
+ return clickableTags.includes(tagName) || isClickable(node.parentElement);
+}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 * as React from 'react';
-import { mockIssue } from '../../../../helpers/testMocks';
-import { Issue } from '../../../../types/types';
-import IssueActionsBar from '../IssueActionsBar';
-
-jest.mock('../../actions', () => ({ updateIssue: jest.fn() }));
-
-it('should render issue correctly', () => {
- const element = shallow(
- <IssueActionsBar
- issue={mockIssue()}
- onAssign={jest.fn()}
- onChange={jest.fn()}
- togglePopup={jest.fn()}
- />
- );
- expect(element).toMatchSnapshot();
-});
-
-it('should render security hotspot correctly', () => {
- const element = shallow(
- <IssueActionsBar
- issue={mockIssue(false, { type: 'SECURITY_HOTSPOT' })}
- onAssign={jest.fn()}
- onChange={jest.fn()}
- togglePopup={jest.fn()}
- />
- );
- expect(element).toMatchSnapshot();
-});
-
-it('should render commentable correctly', () => {
- const element = shallow(
- <IssueActionsBar
- issue={mockIssue(false, { actions: ['comment'] })}
- onAssign={jest.fn()}
- onChange={jest.fn()}
- togglePopup={jest.fn()}
- />
- );
- expect(element).toMatchSnapshot();
-});
-
-it('should render effort correctly', () => {
- const element = shallow(
- <IssueActionsBar
- issue={mockIssue(false, { effort: 'great' })}
- onAssign={jest.fn()}
- onChange={jest.fn()}
- togglePopup={jest.fn()}
- />
- );
- expect(element).toMatchSnapshot();
-});
-
-describe('callback', () => {
- const issue: Issue = mockIssue();
- const onChangeMock = jest.fn();
- const togglePopupMock = jest.fn();
-
- const element = shallow<IssueActionsBar>(
- <IssueActionsBar
- issue={issue}
- onAssign={jest.fn()}
- onChange={onChangeMock}
- togglePopup={togglePopupMock}
- />
- );
-
- beforeEach(() => {
- jest.clearAllMocks();
- });
-
- it('handleTransition should call onChange', () => {
- const instance = element.instance();
- instance.handleTransition(issue);
- expect(onChangeMock).toHaveBeenCalledTimes(1);
- expect(togglePopupMock).toHaveBeenCalledTimes(0);
- });
-
- it('setIssueProperty should call togglePopup', () => {
- const instance = element.instance();
-
- const apiCallMock = jest.fn();
-
- instance.setIssueProperty('author', 'popup', apiCallMock, 'Jay');
- expect(togglePopupMock).toHaveBeenCalledTimes(1);
- expect(apiCallMock).toHaveBeenCalledTimes(1);
- });
-
- it('toggleComment should call togglePopup and update state', () => {
- const instance = element.instance();
-
- expect(element.state('commentAutoTriggered')).toBe(false);
- expect(element.state('commentPlaceholder')).toBe('');
-
- instance.toggleComment(false, 'hold my place', true);
-
- expect(element.state('commentAutoTriggered')).toBe(true);
- expect(element.state('commentPlaceholder')).toBe('hold my place');
-
- expect(togglePopupMock).toHaveBeenCalledTimes(1);
- });
-});
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 * as React from 'react';
-import { mockIssue } from '../../../../helpers/testMocks';
-import { click } from '../../../../helpers/testUtils';
-import IssueAssign from '../IssueAssign';
-
-const issue = mockIssue(false, {
- assignee: 'john',
- assigneeAvatar: 'gravatarhash',
- assigneeName: 'John Doe',
-});
-
-it('should render without the action when the correct rights are missing', () => {
- expect(shallowRender({ canAssign: false })).toMatchSnapshot();
-});
-
-it('should render with the action', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should render a fallback assignee display if assignee info are not available', () => {
- expect(
- shallowRender({ issue: mockIssue(false, { assignee: undefined, assigneeName: undefined }) })
- ).toMatchSnapshot();
-});
-
-it('should open the popup when the button is clicked', () => {
- const togglePopup = jest.fn();
- const element = shallowRender({ togglePopup });
- click(element.find('ButtonLink'));
- expect(togglePopup.mock.calls).toMatchSnapshot();
- element.setProps({ isOpen: true });
- expect(element).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<IssueAssign['props']> = {}) {
- return shallow<IssueAssign>(
- <IssueAssign
- canAssign={true}
- isOpen={false}
- issue={issue}
- onAssign={jest.fn()}
- togglePopup={jest.fn()}
- {...props}
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 * as React from 'react';
-import { click } from '../../../../helpers/testUtils';
-import IssueChangelog from '../IssueChangelog';
-
-const issue = {
- key: 'issuekey',
- author: 'john.david.dalton@gmail.com',
- creationDate: '2017-03-01T09:36:01+0100',
-};
-
-it('should render correctly', () => {
- const element = shallowRender();
- expect(element).toMatchSnapshot();
-});
-
-it('should open the popup when the button is clicked', () => {
- const togglePopup = jest.fn();
- const element = shallowRender({ togglePopup });
- click(element.find('ButtonLink'));
- expect(togglePopup.mock.calls).toMatchSnapshot();
- element.setProps({ isOpen: true });
- expect(element).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<IssueChangelog['props']> = {}) {
- return shallow(
- <IssueChangelog
- creationDate="2017-03-01T09:36:01+0100"
- isOpen={false}
- issue={issue}
- togglePopup={jest.fn()}
- {...props}
- />
- );
-}
* 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 { screen } from '@testing-library/react';
import * as React from 'react';
-import { IssueChangelogDiff as TypeIssueChangelogDiff } from '../../../../types/types';
-import IssueChangelogDiff from '../IssueChangelogDiff';
+import { mockIssueChangelogDiff } from '../../../../helpers/mocks/issues';
+import { renderComponent } from '../../../../helpers/testReactTestingUtils';
+import IssueChangelogDiff, { IssueChangelogDiffProps } from '../IssueChangelogDiff';
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
+jest.mock('../../../../helpers/measures', () => ({
+ formatMeasure: jest
+ .fn()
+ .mockImplementation((value: string, type: string) => `formatted.${value}.as.${type}`),
+}));
-it('should render correctly file diff', () => {
- expect(
- shallowRender({ diff: { key: 'file', oldValue: 'foo/bar.js', newValue: 'bar/baz.js' } })
- ).toMatchSnapshot();
-});
+it.each([
+ ['file', 'issue.change.file_move.oldValue.newValue', undefined],
+ ['from_branch', 'issue.change.from_branch.oldValue.newValue', undefined],
+ ['line', 'issue.changelog.line_removed_X.oldValue', undefined],
+ [
+ 'effort',
+ 'issue.changelog.changed_to.issue.changelog.field.effort.formatted.12.as.WORK_DUR',
+ { newValue: '12', oldValue: undefined },
+ ],
+ [
+ 'effort',
+ 'issue.changelog.removed.issue.changelog.field.effort (issue.changelog.was.formatted.14.as.WORK_DUR)',
+ { newValue: undefined, oldValue: '14' },
+ ],
+ [
+ 'effort',
+ 'issue.changelog.removed.issue.changelog.field.effort',
+ { newValue: undefined, oldValue: undefined },
+ ],
+ [
+ 'assign',
+ 'issue.changelog.changed_to.issue.changelog.field.assign.newValue (issue.changelog.was.oldValue)',
+ undefined,
+ ],
+ ['from_short_branch', 'issue.change.from_non_branch.oldValue.newValue', undefined],
-it('should render correctly branch diff', () => {
- expect(
- shallowRender({
- diff: {
- // Legacy key
- key: 'from_long_branch',
- oldValue: 'foo',
- newValue: 'bar',
- },
- })
- ).toMatchSnapshot();
+ // This should be deprecated. Can this still happen?
+ ['from_long_branch', 'issue.change.from_branch.oldValue.newValue', undefined],
+])(
+ 'should render correctly for "%s" diff types',
+ (key, expected, diff?: Partial<IssueChangelogDiffProps['diff']>) => {
+ renderIssueChangelogDiff({
+ diff: mockIssueChangelogDiff({ key, newValue: 'newValue', oldValue: 'oldValue', ...diff }),
+ });
+ expect(screen.getByText(expected)).toBeInTheDocument();
+ }
+);
- expect(
- shallowRender({
- diff: {
- // Legacy key
- key: 'from_short_branch',
- oldValue: 'foo',
- newValue: 'bar',
- },
- })
- ).toMatchSnapshot();
-
- expect(
- shallowRender({
- diff: {
- key: 'from_branch',
- oldValue: 'foo',
- newValue: 'bar',
- },
- })
- ).toMatchSnapshot();
-});
-
-it('should render correctly line diff', () => {
- expect(shallowRender({ diff: { key: 'line', oldValue: '80' } })).toMatchSnapshot();
-});
-
-it('should render correctly effort diff', () => {
- expect(shallowRender({ diff: { key: 'effort', newValue: '12' } })).toMatchSnapshot();
- expect(
- shallowRender({ diff: { key: 'effort', newValue: '12', oldValue: '10' } })
- ).toMatchSnapshot();
- expect(shallowRender({ diff: { key: 'effort', oldValue: '10' } })).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<{ diff: TypeIssueChangelogDiff }> = {}) {
- return shallow(<IssueChangelogDiff diff={{ key: 'foo' }} {...props} />);
+function renderIssueChangelogDiff(props: Partial<IssueChangelogDiffProps> = {}) {
+ return renderComponent(<IssueChangelogDiff diff={mockIssueChangelogDiff()} {...props} />);
}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 * as React from 'react';
-import { click } from '../../../../helpers/testUtils';
-import { IssueComment } from '../../../../types/types';
-import IssueCommentLine from '../IssueCommentLine';
-
-const comment: IssueComment = {
- author: 'john.doe',
- authorActive: true,
- authorAvatar: 'gravatarhash',
- authorName: 'John Doe',
- createdAt: '2017-03-01T09:36:01+0100',
- htmlText: '<b>test</b>',
- key: 'comment-key',
- markdown: '*test*',
- updatable: true,
-};
-
-it('should render correctly a comment that is updatable', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should render correctly a comment that is not updatable', () => {
- expect(shallowRender({ comment: { ...comment, updatable: false } })).toMatchSnapshot();
-});
-
-it('should open the right popups when the buttons are clicked', () => {
- const wrapper = shallowRender();
- click(wrapper.find('.js-issue-comment-edit'));
- expect(wrapper.state()).toMatchSnapshot();
- click(wrapper.find('.js-issue-comment-delete'));
- expect(wrapper.state()).toMatchSnapshot();
- wrapper.update();
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should render correctly a comment with a deleted author', () => {
- expect(
- shallowRender({
- comment: { ...comment, authorActive: false, authorName: undefined },
- }).find('.issue-comment-author')
- ).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<IssueCommentLine['props']> = {}) {
- return shallow(
- <IssueCommentLine comment={comment} onDelete={jest.fn()} onEdit={jest.fn()} {...props} />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 * as React from 'react';
-import { mockBranch } from '../../../../helpers/mocks/branch-like';
-import { mockIssue } from '../../../../helpers/testMocks';
-import { RuleStatus } from '../../../../types/rules';
-import IssueMessage, { IssueMessageProps } from '../IssueMessage';
-
-jest.mock('react', () => {
- return {
- ...jest.requireActual('react'),
- useContext: jest
- .fn()
- .mockImplementation(() => ({ externalRulesRepoNames: {}, openRule: jest.fn() })),
- };
-});
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot('default');
- expect(shallowRender({ issue: mockIssue(false, { externalRuleEngine: 'js' }) })).toMatchSnapshot(
- 'with engine info'
- );
- expect(shallowRender({ issue: mockIssue(false, { quickFixAvailable: true }) })).toMatchSnapshot(
- 'with quick fix'
- );
- expect(
- shallowRender({ issue: mockIssue(false, { ruleStatus: RuleStatus.Deprecated }) })
- ).toMatchSnapshot('is deprecated rule');
- expect(
- shallowRender({ issue: mockIssue(false, { ruleStatus: RuleStatus.Removed }) })
- ).toMatchSnapshot('is removed rule');
- expect(shallowRender({ displayWhyIsThisAnIssue: false })).toMatchSnapshot(
- 'hide why is it an issue'
- );
-});
-
-function shallowRender(props: Partial<IssueMessageProps> = {}) {
- return shallow<IssueMessageProps>(
- <IssueMessage
- issue={mockIssue(false, {
- message: 'Reduce the number of conditional operators (4) used in the expression',
- })}
- displayWhyIsThisAnIssue={true}
- branchLike={mockBranch()}
- {...props}
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 * as React from 'react';
-import { click } from '../../../../helpers/testUtils';
-import IssueSeverity from '../IssueSeverity';
-
-const issue = { severity: 'BLOCKER' };
-
-it('should render without the action when the correct rights are missing', () => {
- expect(shallowRender({ canSetSeverity: false })).toMatchSnapshot();
-});
-
-it('should render with the action', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should open the popup when the button is clicked', () => {
- const togglePopup = jest.fn();
- const element = shallowRender({ togglePopup });
- click(element.find('ButtonLink'));
- expect(togglePopup.mock.calls).toMatchSnapshot();
- element.setProps({ isOpen: true });
- expect(element).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<IssueSeverity['props']> = {}) {
- return shallow(
- <IssueSeverity
- canSetSeverity={true}
- isOpen={false}
- issue={issue}
- setIssueProperty={jest.fn()}
- togglePopup={jest.fn()}
- {...props}
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 * as React from 'react';
-import { mockBranch } from '../../../../helpers/mocks/branch-like';
-import { mockIssue } from '../../../../helpers/testMocks';
-import IssueTitleBar, { IssueTitleBarProps } from '../IssueTitleBar';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot('default');
- expect(shallowRender({ onFilter: jest.fn() })).toMatchSnapshot('with filter');
- expect(shallowRender({ displayLocationsCount: true, issue: mockIssue(true) })).toMatchSnapshot(
- 'with multi locations'
- );
- expect(
- shallowRender({
- branchLike: mockBranch(),
- displayLocationsCount: true,
- displayLocationsLink: true,
- issue: mockIssue(true),
- })
- ).toMatchSnapshot('with multi locations and link');
-});
-
-function shallowRender(props: Partial<IssueTitleBarProps> = {}) {
- return shallow<IssueTitleBarProps>(
- <IssueTitleBar
- issue={mockIssue(false, { externalRuleEngine: 'foo' })}
- togglePopup={jest.fn()}
- {...props}
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 * as React from 'react';
-import IssueTransition from '../IssueTransition';
-
-const issue: IssueTransition['props']['issue'] = {
- key: 'foo1234',
- transitions: ['confirm', 'resolve', 'falsepositive', 'wontfix'],
- status: 'OPEN',
- type: 'BUG',
-};
-
-it('should render without the action when there is no transitions', () => {
- expect(
- shallowRender({
- hasTransitions: false,
- issue: { key: 'foo1234', transitions: [], status: 'CLOSED', type: 'BUG' },
- })
- ).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<IssueTransition['props']> = {}) {
- return shallow(
- <IssueTransition
- hasTransitions={true}
- isOpen={false}
- issue={issue}
- onChange={jest.fn()}
- togglePopup={jest.fn()}
- {...props}
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 * as React from 'react';
-import { Issue } from '../../../../types/types';
-import IssueType from '../IssueType';
-
-const issue: Pick<Issue, 'type'> = { type: 'BUG' };
-
-it('should render without the action when the correct rights are missing', () => {
- expect(shallowRender({ canSetType: false })).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<IssueType['props']> = {}) {
- return shallow(
- <IssueType
- canSetType={true}
- isOpen={false}
- issue={issue}
- setIssueProperty={jest.fn()}
- togglePopup={jest.fn()}
- {...props}
- />
- );
-}
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render commentable correctly 1`] = `
-<div
- className="issue-actions"
->
- <div
- className="issue-meta-list"
- >
- <div
- className="issue-meta"
- >
- <IssueType
- canSetType={false}
- isOpen={false}
- issue={
- {
- "actions": [
- "comment",
- ],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- setIssueProperty={[Function]}
- togglePopup={[MockFunction]}
- />
- </div>
- <div
- className="issue-meta"
- >
- <IssueSeverity
- canSetSeverity={false}
- isOpen={false}
- issue={
- {
- "actions": [
- "comment",
- ],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- setIssueProperty={[Function]}
- togglePopup={[MockFunction]}
- />
- </div>
- <div
- className="issue-meta"
- >
- <IssueTransition
- hasTransitions={false}
- isOpen={false}
- issue={
- {
- "actions": [
- "comment",
- ],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- onChange={[Function]}
- togglePopup={[MockFunction]}
- />
- </div>
- <div
- className="issue-meta"
- >
- <IssueAssign
- canAssign={false}
- isOpen={false}
- issue={
- {
- "actions": [
- "comment",
- ],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- onAssign={[MockFunction]}
- togglePopup={[MockFunction]}
- />
- </div>
- <IssueCommentAction
- canComment={true}
- commentAutoTriggered={false}
- commentPlaceholder=""
- issueKey="AVsae-CQS-9G3txfbFN2"
- onChange={[MockFunction]}
- toggleComment={[Function]}
- />
- </div>
- <div
- className="list-inline"
- >
- <div
- className="issue-meta js-issue-tags"
- >
- <IssueTags
- canSetTags={false}
- isOpen={false}
- issue={
- {
- "actions": [
- "comment",
- ],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- onChange={[MockFunction]}
- togglePopup={[MockFunction]}
- />
- </div>
- </div>
-</div>
-`;
-
-exports[`should render effort correctly 1`] = `
-<div
- className="issue-actions"
->
- <div
- className="issue-meta-list"
- >
- <div
- className="issue-meta"
- >
- <IssueType
- canSetType={false}
- isOpen={false}
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "effort": "great",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- setIssueProperty={[Function]}
- togglePopup={[MockFunction]}
- />
- </div>
- <div
- className="issue-meta"
- >
- <IssueSeverity
- canSetSeverity={false}
- isOpen={false}
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "effort": "great",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- setIssueProperty={[Function]}
- togglePopup={[MockFunction]}
- />
- </div>
- <div
- className="issue-meta"
- >
- <IssueTransition
- hasTransitions={false}
- isOpen={false}
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "effort": "great",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- onChange={[Function]}
- togglePopup={[MockFunction]}
- />
- </div>
- <div
- className="issue-meta"
- >
- <IssueAssign
- canAssign={false}
- isOpen={false}
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "effort": "great",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- onAssign={[MockFunction]}
- togglePopup={[MockFunction]}
- />
- </div>
- <div
- className="issue-meta"
- >
- <span
- className="issue-meta-label"
- >
- issue.x_effort.great
- </span>
- </div>
- </div>
- <div
- className="list-inline"
- >
- <div
- className="issue-meta js-issue-tags"
- >
- <IssueTags
- canSetTags={false}
- isOpen={false}
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "effort": "great",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- onChange={[MockFunction]}
- togglePopup={[MockFunction]}
- />
- </div>
- </div>
-</div>
-`;
-
-exports[`should render issue correctly 1`] = `
-<div
- className="issue-actions"
->
- <div
- className="issue-meta-list"
- >
- <div
- className="issue-meta"
- >
- <IssueType
- canSetType={false}
- isOpen={false}
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- setIssueProperty={[Function]}
- togglePopup={[MockFunction]}
- />
- </div>
- <div
- className="issue-meta"
- >
- <IssueSeverity
- canSetSeverity={false}
- isOpen={false}
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- setIssueProperty={[Function]}
- togglePopup={[MockFunction]}
- />
- </div>
- <div
- className="issue-meta"
- >
- <IssueTransition
- hasTransitions={false}
- isOpen={false}
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- onChange={[Function]}
- togglePopup={[MockFunction]}
- />
- </div>
- <div
- className="issue-meta"
- >
- <IssueAssign
- canAssign={false}
- isOpen={false}
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- onAssign={[MockFunction]}
- togglePopup={[MockFunction]}
- />
- </div>
- </div>
- <div
- className="list-inline"
- >
- <div
- className="issue-meta js-issue-tags"
- >
- <IssueTags
- canSetTags={false}
- isOpen={false}
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- onChange={[MockFunction]}
- togglePopup={[MockFunction]}
- />
- </div>
- </div>
-</div>
-`;
-
-exports[`should render security hotspot correctly 1`] = `
-<div
- className="issue-actions"
->
- <div
- className="issue-meta-list"
- >
- <div
- className="issue-meta"
- >
- <IssueType
- canSetType={false}
- isOpen={false}
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "SECURITY_HOTSPOT",
- }
- }
- setIssueProperty={[Function]}
- togglePopup={[MockFunction]}
- />
- </div>
- <div
- className="issue-meta"
- >
- <IssueTransition
- hasTransitions={false}
- isOpen={false}
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "SECURITY_HOTSPOT",
- }
- }
- onChange={[Function]}
- togglePopup={[MockFunction]}
- />
- </div>
- <div
- className="issue-meta"
- >
- <IssueAssign
- canAssign={false}
- isOpen={false}
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "SECURITY_HOTSPOT",
- }
- }
- onAssign={[MockFunction]}
- togglePopup={[MockFunction]}
- />
- </div>
- </div>
- <div
- className="list-inline"
- >
- <div
- className="issue-meta js-issue-tags"
- >
- <IssueTags
- canSetTags={false}
- isOpen={false}
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "SECURITY_HOTSPOT",
- }
- }
- onChange={[MockFunction]}
- togglePopup={[MockFunction]}
- />
- </div>
- </div>
-</div>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should open the popup when the button is clicked 1`] = `
-[
- [
- "assign",
- undefined,
- ],
-]
-`;
-
-exports[`should open the popup when the button is clicked 2`] = `
-<div
- className="dropdown"
->
- <Toggler
- closeOnEscape={true}
- onRequestClose={[Function]}
- open={true}
- overlay={
- <withCurrentUserContext(SetAssigneePopup)
- onSelect={[MockFunction]}
- />
- }
- >
- <ButtonLink
- aria-expanded={true}
- aria-label="issue.assign.assigned_to_x_click_to_change.John Doe"
- className="issue-action issue-action-with-options js-issue-assign"
- onClick={[Function]}
- >
- <span
- className="text-top"
- >
- <withAppStateContext(Avatar)
- className="little-spacer-right"
- hash="gravatarhash"
- name=""
- size={16}
- />
- </span>
- <span
- className="issue-meta-label"
- title="John Doe"
- >
- John Doe
- </span>
- <DropdownIcon
- className="little-spacer-left"
- />
- </ButtonLink>
- </Toggler>
-</div>
-`;
-
-exports[`should render a fallback assignee display if assignee info are not available 1`] = `
-<div
- className="dropdown"
->
- <Toggler
- closeOnEscape={true}
- onRequestClose={[Function]}
- open={false}
- overlay={
- <withCurrentUserContext(SetAssigneePopup)
- onSelect={[MockFunction]}
- />
- }
- >
- <ButtonLink
- aria-expanded={false}
- aria-label="issue.assign.unassigned_click_to_assign"
- className="issue-action issue-action-with-options js-issue-assign"
- onClick={[Function]}
- >
- <span
- className="issue-meta-label"
- >
- unassigned
- </span>
- <DropdownIcon
- className="little-spacer-left"
- />
- </ButtonLink>
- </Toggler>
-</div>
-`;
-
-exports[`should render with the action 1`] = `
-<div
- className="dropdown"
->
- <Toggler
- closeOnEscape={true}
- onRequestClose={[Function]}
- open={false}
- overlay={
- <withCurrentUserContext(SetAssigneePopup)
- onSelect={[MockFunction]}
- />
- }
- >
- <ButtonLink
- aria-expanded={false}
- aria-label="issue.assign.assigned_to_x_click_to_change.John Doe"
- className="issue-action issue-action-with-options js-issue-assign"
- onClick={[Function]}
- >
- <span
- className="text-top"
- >
- <withAppStateContext(Avatar)
- className="little-spacer-right"
- hash="gravatarhash"
- name=""
- size={16}
- />
- </span>
- <span
- className="issue-meta-label"
- title="John Doe"
- >
- John Doe
- </span>
- <DropdownIcon
- className="little-spacer-left"
- />
- </ButtonLink>
- </Toggler>
-</div>
-`;
-
-exports[`should render without the action when the correct rights are missing 1`] = `
-<Fragment>
- <span
- className="text-top"
- >
- <withAppStateContext(Avatar)
- className="little-spacer-right"
- hash="gravatarhash"
- name=""
- size={16}
- />
- </span>
- <span
- className="issue-meta-label"
- title="John Doe"
- >
- John Doe
- </span>
-</Fragment>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should open the popup when the button is clicked 1`] = `
-[
- [
- "changelog",
- undefined,
- ],
-]
-`;
-
-exports[`should open the popup when the button is clicked 2`] = `
-<div
- className="dropdown"
->
- <Toggler
- onRequestClose={[Function]}
- open={true}
- overlay={
- <ChangelogPopup
- issue={
- {
- "author": "john.david.dalton@gmail.com",
- "creationDate": "2017-03-01T09:36:01+0100",
- "key": "issuekey",
- }
- }
- />
- }
- >
- <ButtonLink
- aria-expanded={true}
- className="issue-action issue-action-with-options js-issue-show-changelog"
- onClick={[Function]}
- >
- <span
- className="issue-meta-label"
- >
- <DateFromNow
- date="2017-03-01T09:36:01+0100"
- />
- </span>
- <DropdownIcon
- className="little-spacer-left"
- />
- </ButtonLink>
- </Toggler>
-</div>
-`;
-
-exports[`should render correctly 1`] = `
-<div
- className="dropdown"
->
- <Toggler
- onRequestClose={[Function]}
- open={false}
- overlay={
- <ChangelogPopup
- issue={
- {
- "author": "john.david.dalton@gmail.com",
- "creationDate": "2017-03-01T09:36:01+0100",
- "key": "issuekey",
- }
- }
- />
- }
- >
- <ButtonLink
- aria-expanded={false}
- className="issue-action issue-action-with-options js-issue-show-changelog"
- onClick={[Function]}
- >
- <span
- className="issue-meta-label"
- >
- <DateFromNow
- date="2017-03-01T09:36:01+0100"
- />
- </span>
- <DropdownIcon
- className="little-spacer-left"
- />
- </ButtonLink>
- </Toggler>
-</div>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<p>
- issue.changelog.removed.issue.changelog.field.foo
-</p>
-`;
-
-exports[`should render correctly branch diff 1`] = `
-<p>
- issue.change.from_branch.foo.bar
-</p>
-`;
-
-exports[`should render correctly branch diff 2`] = `
-<p>
- issue.change.from_non_branch.foo.bar
-</p>
-`;
-
-exports[`should render correctly branch diff 3`] = `
-<p>
- issue.change.from_branch.foo.bar
-</p>
-`;
-
-exports[`should render correctly effort diff 1`] = `
-<p>
- issue.changelog.changed_to.issue.changelog.field.effort.work_duration.x_minutes.12
-</p>
-`;
-
-exports[`should render correctly effort diff 2`] = `
-<p>
- issue.changelog.changed_to.issue.changelog.field.effort.work_duration.x_minutes.12 (issue.changelog.was.work_duration.x_minutes.10)
-</p>
-`;
-
-exports[`should render correctly effort diff 3`] = `
-<p>
- issue.changelog.removed.issue.changelog.field.effort (issue.changelog.was.work_duration.x_minutes.10)
-</p>
-`;
-
-exports[`should render correctly file diff 1`] = `
-<p>
- issue.change.file_move.foo/bar.js.bar/baz.js
-</p>
-`;
-
-exports[`should render correctly line diff 1`] = `
-<p>
- issue.changelog.line_removed_X.80
-</p>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should open the right popups when the buttons are clicked 1`] = `
-{
- "openPopup": "edit",
-}
-`;
-
-exports[`should open the right popups when the buttons are clicked 2`] = `
-{
- "openPopup": "delete",
-}
-`;
-
-exports[`should open the right popups when the buttons are clicked 3`] = `
-<div
- className="issue-comment"
->
- <div
- className="issue-comment-author"
- title="John Doe"
- >
- <withAppStateContext(Avatar)
- className="little-spacer-right"
- hash="gravatarhash"
- name="John Doe"
- size={16}
- />
- John Doe
- </div>
- <div
- className="issue-comment-text markdown"
- dangerouslySetInnerHTML={
- {
- "__html": "<b>test</b>",
- }
- }
- />
- <div
- className="issue-comment-age"
- >
- <span
- className="a11y-hidden"
- >
- issue.comment.posted_on
- </span>
- <DateFromNow
- date="2017-03-01T09:36:01+0100"
- />
- </div>
- <div
- className="issue-comment-actions"
- >
- <div
- className="dropdown"
- >
- <Toggler
- closeOnClickOutside={false}
- onRequestClose={[Function]}
- open={false}
- overlay={
- <CommentPopup
- comment={
- {
- "author": "john.doe",
- "authorActive": true,
- "authorAvatar": "gravatarhash",
- "authorName": "John Doe",
- "createdAt": "2017-03-01T09:36:01+0100",
- "htmlText": "<b>test</b>",
- "key": "comment-key",
- "markdown": "*test*",
- "updatable": true,
- }
- }
- onComment={[Function]}
- placeholder=""
- placement="bottom-right"
- toggleComment={[Function]}
- />
- }
- >
- <EditButton
- aria-label="issue.comment.edit"
- className="js-issue-comment-edit button-small"
- onClick={[Function]}
- />
- </Toggler>
- </div>
- <div
- className="dropdown"
- >
- <Toggler
- onRequestClose={[Function]}
- open={true}
- overlay={
- <CommentDeletePopup
- onDelete={[Function]}
- />
- }
- >
- <DeleteButton
- aria-label="issue.comment.delete"
- className="js-issue-comment-delete button-small"
- onClick={[Function]}
- />
- </Toggler>
- </div>
- </div>
-</div>
-`;
-
-exports[`should render correctly a comment that is not updatable 1`] = `
-<div
- className="issue-comment"
->
- <div
- className="issue-comment-author"
- title="John Doe"
- >
- <withAppStateContext(Avatar)
- className="little-spacer-right"
- hash="gravatarhash"
- name="John Doe"
- size={16}
- />
- John Doe
- </div>
- <div
- className="issue-comment-text markdown"
- dangerouslySetInnerHTML={
- {
- "__html": "<b>test</b>",
- }
- }
- />
- <div
- className="issue-comment-age"
- >
- <span
- className="a11y-hidden"
- >
- issue.comment.posted_on
- </span>
- <DateFromNow
- date="2017-03-01T09:36:01+0100"
- />
- </div>
- <div
- className="issue-comment-actions"
- />
-</div>
-`;
-
-exports[`should render correctly a comment that is updatable 1`] = `
-<div
- className="issue-comment"
->
- <div
- className="issue-comment-author"
- title="John Doe"
- >
- <withAppStateContext(Avatar)
- className="little-spacer-right"
- hash="gravatarhash"
- name="John Doe"
- size={16}
- />
- John Doe
- </div>
- <div
- className="issue-comment-text markdown"
- dangerouslySetInnerHTML={
- {
- "__html": "<b>test</b>",
- }
- }
- />
- <div
- className="issue-comment-age"
- >
- <span
- className="a11y-hidden"
- >
- issue.comment.posted_on
- </span>
- <DateFromNow
- date="2017-03-01T09:36:01+0100"
- />
- </div>
- <div
- className="issue-comment-actions"
- >
- <div
- className="dropdown"
- >
- <Toggler
- closeOnClickOutside={false}
- onRequestClose={[Function]}
- open={false}
- overlay={
- <CommentPopup
- comment={
- {
- "author": "john.doe",
- "authorActive": true,
- "authorAvatar": "gravatarhash",
- "authorName": "John Doe",
- "createdAt": "2017-03-01T09:36:01+0100",
- "htmlText": "<b>test</b>",
- "key": "comment-key",
- "markdown": "*test*",
- "updatable": true,
- }
- }
- onComment={[Function]}
- placeholder=""
- placement="bottom-right"
- toggleComment={[Function]}
- />
- }
- >
- <EditButton
- aria-label="issue.comment.edit"
- className="js-issue-comment-edit button-small"
- onClick={[Function]}
- />
- </Toggler>
- </div>
- <div
- className="dropdown"
- >
- <Toggler
- onRequestClose={[Function]}
- open={false}
- overlay={
- <CommentDeletePopup
- onDelete={[Function]}
- />
- }
- >
- <DeleteButton
- aria-label="issue.comment.delete"
- className="js-issue-comment-delete button-small"
- onClick={[Function]}
- />
- </Toggler>
- </div>
- </div>
-</div>
-`;
-
-exports[`should render correctly a comment with a deleted author 1`] = `
-<div
- className="issue-comment-author"
- title="user.x_deleted.john.doe"
->
- <withAppStateContext(Avatar)
- className="little-spacer-right"
- hash="gravatarhash"
- name="john.doe"
- size={16}
- />
- user.x_deleted.john.doe
-</div>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: default 1`] = `
-<Fragment>
- <div
- className="display-inline-flex-center issue-message break-word"
- >
- <span
- className="spacer-right"
- >
- <IssueMessageHighlighting
- message="Reduce the number of conditional operators (4) used in the expression"
- />
- </span>
- <IssueMessageTags />
- </div>
- <ForwardRef(Link)
- aria-label="issue.why_this_issue.long"
- className="spacer-right"
- target="_blank"
- to={
- {
- "hash": "",
- "pathname": "/project/issues",
- "search": "?branch=branch-6.7&files=main.js&open=AVsae-CQS-9G3txfbFN2&resolved=false&why=1&id=myproject",
- }
- }
- >
- issue.why_this_issue
- </ForwardRef(Link)>
-</Fragment>
-`;
-
-exports[`should render correctly: hide why is it an issue 1`] = `
-<Fragment>
- <div
- className="display-inline-flex-center issue-message break-word"
- >
- <span
- className="spacer-right"
- >
- <IssueMessageHighlighting
- message="Reduce the number of conditional operators (4) used in the expression"
- />
- </span>
- <IssueMessageTags />
- </div>
-</Fragment>
-`;
-
-exports[`should render correctly: is deprecated rule 1`] = `
-<Fragment>
- <div
- className="display-inline-flex-center issue-message break-word"
- >
- <span
- className="spacer-right"
- >
- <IssueMessageHighlighting
- message="Reduce the number of conditional operators (4) used in the expression"
- />
- </span>
- <IssueMessageTags
- ruleStatus="DEPRECATED"
- />
- </div>
- <ForwardRef(Link)
- aria-label="issue.why_this_issue.long"
- className="spacer-right"
- target="_blank"
- to={
- {
- "hash": "",
- "pathname": "/project/issues",
- "search": "?branch=branch-6.7&files=main.js&open=AVsae-CQS-9G3txfbFN2&resolved=false&why=1&id=myproject",
- }
- }
- >
- issue.why_this_issue
- </ForwardRef(Link)>
-</Fragment>
-`;
-
-exports[`should render correctly: is removed rule 1`] = `
-<Fragment>
- <div
- className="display-inline-flex-center issue-message break-word"
- >
- <span
- className="spacer-right"
- >
- <IssueMessageHighlighting
- message="Reduce the number of conditional operators (4) used in the expression"
- />
- </span>
- <IssueMessageTags
- ruleStatus="REMOVED"
- />
- </div>
- <ForwardRef(Link)
- aria-label="issue.why_this_issue.long"
- className="spacer-right"
- target="_blank"
- to={
- {
- "hash": "",
- "pathname": "/project/issues",
- "search": "?branch=branch-6.7&files=main.js&open=AVsae-CQS-9G3txfbFN2&resolved=false&why=1&id=myproject",
- }
- }
- >
- issue.why_this_issue
- </ForwardRef(Link)>
-</Fragment>
-`;
-
-exports[`should render correctly: with engine info 1`] = `
-<Fragment>
- <div
- className="display-inline-flex-center issue-message break-word"
- >
- <span
- className="spacer-right"
- >
- <IssueMessageHighlighting
- message="Reduce the number of conditional operators (4) used in the expression"
- />
- </span>
- <IssueMessageTags
- engine="js"
- />
- </div>
- <ForwardRef(Link)
- aria-label="issue.why_this_issue.long"
- className="spacer-right"
- target="_blank"
- to={
- {
- "hash": "",
- "pathname": "/project/issues",
- "search": "?branch=branch-6.7&files=main.js&open=AVsae-CQS-9G3txfbFN2&resolved=false&why=1&id=myproject",
- }
- }
- >
- issue.why_this_issue
- </ForwardRef(Link)>
-</Fragment>
-`;
-
-exports[`should render correctly: with quick fix 1`] = `
-<Fragment>
- <div
- className="display-inline-flex-center issue-message break-word"
- >
- <span
- className="spacer-right"
- >
- <IssueMessageHighlighting
- message="Reduce the number of conditional operators (4) used in the expression"
- />
- </span>
- <IssueMessageTags
- quickFixAvailable={true}
- />
- </div>
- <ForwardRef(Link)
- aria-label="issue.why_this_issue.long"
- className="spacer-right"
- target="_blank"
- to={
- {
- "hash": "",
- "pathname": "/project/issues",
- "search": "?branch=branch-6.7&files=main.js&open=AVsae-CQS-9G3txfbFN2&resolved=false&why=1&id=myproject",
- }
- }
- >
- issue.why_this_issue
- </ForwardRef(Link)>
-</Fragment>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should open the popup when the button is clicked 1`] = `
-[
- [
- "set-severity",
- undefined,
- ],
-]
-`;
-
-exports[`should open the popup when the button is clicked 2`] = `
-<div
- className="dropdown"
->
- <Toggler
- onRequestClose={[Function]}
- open={true}
- overlay={
- <SetSeverityPopup
- issue={
- {
- "severity": "BLOCKER",
- }
- }
- onSelect={[Function]}
- />
- }
- >
- <ButtonLink
- aria-expanded={true}
- aria-label="issue.severity.severity_x_click_to_change.severity.BLOCKER"
- className="issue-action issue-action-with-options js-issue-set-severity"
- onClick={[Function]}
- >
- <SeverityHelper
- className="issue-meta-label"
- severity="BLOCKER"
- />
- <DropdownIcon
- className="little-spacer-left"
- />
- </ButtonLink>
- </Toggler>
-</div>
-`;
-
-exports[`should render with the action 1`] = `
-<div
- className="dropdown"
->
- <Toggler
- onRequestClose={[Function]}
- open={false}
- overlay={
- <SetSeverityPopup
- issue={
- {
- "severity": "BLOCKER",
- }
- }
- onSelect={[Function]}
- />
- }
- >
- <ButtonLink
- aria-expanded={false}
- aria-label="issue.severity.severity_x_click_to_change.severity.BLOCKER"
- className="issue-action issue-action-with-options js-issue-set-severity"
- onClick={[Function]}
- >
- <SeverityHelper
- className="issue-meta-label"
- severity="BLOCKER"
- />
- <DropdownIcon
- className="little-spacer-left"
- />
- </ButtonLink>
- </Toggler>
-</div>
-`;
-
-exports[`should render without the action when the correct rights are missing 1`] = `
-<SeverityHelper
- className="issue-meta-label"
- severity="BLOCKER"
-/>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly: default 1`] = `
-<div
- className="issue-row"
->
- <IssueMessage
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "externalRuleEngine": "foo",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- />
- <div
- className="issue-row-meta"
- >
- <div
- className="issue-meta-list"
- >
- <div
- className="issue-meta"
- >
- <IssueChangelog
- creationDate="2017-03-01T09:36:01+0100"
- isOpen={false}
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "externalRuleEngine": "foo",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- togglePopup={[MockFunction]}
- />
- </div>
- <div
- className="issue-meta"
- >
- <span
- className="issue-meta-label"
- title="line_number"
- >
- L
- 26
- </span>
- </div>
- <div
- className="issue-meta"
- >
- <ForwardRef(Link)
- className="js-issue-permalink link-no-underline"
- target="_blank"
- title="permalink"
- to={
- {
- "hash": "",
- "pathname": "/project/issues",
- "search": "?issues=AVsae-CQS-9G3txfbFN2&open=AVsae-CQS-9G3txfbFN2&id=myproject",
- }
- }
- >
- <LinkIcon />
- </ForwardRef(Link)>
- </div>
- </div>
- </div>
-</div>
-`;
-
-exports[`should render correctly: with filter 1`] = `
-<div
- className="issue-row"
->
- <IssueMessage
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "externalRuleEngine": "foo",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- />
- <div
- className="issue-row-meta"
- >
- <div
- className="issue-meta-list"
- >
- <div
- className="issue-meta"
- >
- <IssueChangelog
- creationDate="2017-03-01T09:36:01+0100"
- isOpen={false}
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "externalRuleEngine": "foo",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- togglePopup={[MockFunction]}
- />
- </div>
- <div
- className="issue-meta"
- >
- <span
- className="issue-meta-label"
- title="line_number"
- >
- L
- 26
- </span>
- </div>
- <div
- className="issue-meta"
- >
- <ForwardRef(Link)
- className="js-issue-permalink link-no-underline"
- target="_blank"
- title="permalink"
- to={
- {
- "hash": "",
- "pathname": "/project/issues",
- "search": "?issues=AVsae-CQS-9G3txfbFN2&open=AVsae-CQS-9G3txfbFN2&id=myproject",
- }
- }
- >
- <LinkIcon />
- </ForwardRef(Link)>
- </div>
- <div
- className="issue-meta"
- >
- <SimilarIssuesFilter
- isOpen={false}
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "externalRuleEngine": "foo",
- "flows": [],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- onFilter={[MockFunction]}
- togglePopup={[MockFunction]}
- />
- </div>
- </div>
- </div>
-</div>
-`;
-
-exports[`should render correctly: with multi locations 1`] = `
-<div
- className="issue-row"
->
- <IssueMessage
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [
- [
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- ],
- [
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- ],
- ],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- ],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- />
- <div
- className="issue-row-meta"
- >
- <div
- className="issue-meta-list"
- >
- <div
- className="issue-meta"
- >
- <IssueChangelog
- creationDate="2017-03-01T09:36:01+0100"
- isOpen={false}
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [
- [
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- ],
- [
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- ],
- ],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- ],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- togglePopup={[MockFunction]}
- />
- </div>
- <div
- className="issue-meta"
- >
- <span
- className="issue-meta-label"
- title="line_number"
- >
- L
- 26
- </span>
- </div>
- <div
- className="issue-meta"
- >
- <Tooltip
- overlay="issue.this_issue_involves_x_code_locations.7"
- >
- <LocationIndex>
- 7
- </LocationIndex>
- </Tooltip>
- </div>
- <div
- className="issue-meta"
- >
- <ForwardRef(Link)
- className="js-issue-permalink link-no-underline"
- target="_blank"
- title="permalink"
- to={
- {
- "hash": "",
- "pathname": "/project/issues",
- "search": "?issues=AVsae-CQS-9G3txfbFN2&open=AVsae-CQS-9G3txfbFN2&id=myproject",
- }
- }
- >
- <LinkIcon />
- </ForwardRef(Link)>
- </div>
- </div>
- </div>
-</div>
-`;
-
-exports[`should render correctly: with multi locations and link 1`] = `
-<div
- className="issue-row"
->
- <IssueMessage
- branchLike={
- {
- "analysisDate": "2018-01-01",
- "excludedFromPurge": true,
- "isMain": false,
- "name": "branch-6.7",
- }
- }
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [
- [
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- ],
- [
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- ],
- ],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- ],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- />
- <div
- className="issue-row-meta"
- >
- <div
- className="issue-meta-list"
- >
- <div
- className="issue-meta"
- >
- <IssueChangelog
- creationDate="2017-03-01T09:36:01+0100"
- isOpen={false}
- issue={
- {
- "actions": [],
- "component": "main.js",
- "componentEnabled": true,
- "componentLongName": "main.js",
- "componentQualifier": "FIL",
- "componentUuid": "foo1234",
- "creationDate": "2017-03-01T09:36:01+0100",
- "flows": [
- [
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- ],
- [
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- ],
- ],
- "flowsWithType": [],
- "key": "AVsae-CQS-9G3txfbFN2",
- "line": 25,
- "message": "Reduce the number of conditional operators (4) used in the expression",
- "project": "myproject",
- "projectKey": "foo",
- "projectName": "Foo",
- "rule": "javascript:S1067",
- "ruleName": "foo",
- "scope": "MAIN",
- "secondaryLocations": [
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- {
- "component": "main.js",
- "textRange": {
- "endLine": 2,
- "endOffset": 2,
- "startLine": 1,
- "startOffset": 1,
- },
- },
- ],
- "severity": "MAJOR",
- "status": "OPEN",
- "textRange": {
- "endLine": 26,
- "endOffset": 15,
- "startLine": 25,
- "startOffset": 0,
- },
- "transitions": [],
- "type": "BUG",
- }
- }
- togglePopup={[MockFunction]}
- />
- </div>
- <div
- className="issue-meta"
- >
- <span
- className="issue-meta-label"
- title="line_number"
- >
- L
- 26
- </span>
- </div>
- <div
- className="issue-meta"
- >
- <ForwardRef(Link)
- target="_blank"
- to={
- {
- "hash": "",
- "pathname": "/project/issues",
- "search": "?branch=branch-6.7&issues=AVsae-CQS-9G3txfbFN2&open=AVsae-CQS-9G3txfbFN2&id=myproject",
- }
- }
- >
- <Tooltip
- overlay="issue.this_issue_involves_x_code_locations.7"
- >
- <LocationIndex>
- 7
- </LocationIndex>
- </Tooltip>
- </ForwardRef(Link)>
- </div>
- <div
- className="issue-meta"
- >
- <ForwardRef(Link)
- className="js-issue-permalink link-no-underline"
- target="_blank"
- title="permalink"
- to={
- {
- "hash": "",
- "pathname": "/project/issues",
- "search": "?branch=branch-6.7&issues=AVsae-CQS-9G3txfbFN2&open=AVsae-CQS-9G3txfbFN2&id=myproject",
- }
- }
- >
- <LinkIcon />
- </ForwardRef(Link)>
- </div>
- </div>
- </div>
-</div>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render without the action when there is no transitions 1`] = `
-<StatusHelper
- className="issue-meta-label"
- status="CLOSED"
-/>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render without the action when the correct rights are missing 1`] = `
-<span>
- <IssueTypeIcon
- className="little-spacer-right"
- query="BUG"
- />
- issue.type.BUG
-</span>
-`;
<DateTimeFormatter date={item.creationDate} />
</td>
<td className="text-left text-top">
- <p>
+ <div>
{userName && (
<>
<Avatar
'issue.changelog.webhook_source',
item.webhookSource
)}
- </p>
+ </div>
{item.diffs.map((diff) => (
<IssueChangelogDiff diff={diff} key={diff.key} />
))}
style={{ resize: 'vertical' }}
placeholder={placeholder}
aria-label={translate('issue.comment.enter_comment')}
- onChange={(event: React.ChangeEvent<HTMLTextAreaElement>) =>
- setEditComment(event.target.value)
- }
+ onChange={(event: React.ChangeEvent<HTMLTextAreaElement>) => {
+ setEditComment(event.target.value);
+ }}
onKeyDown={(event: React.KeyboardEvent) => {
if (event.nativeEvent.key === KeyboardKeys.Enter && (event.metaKey || event.ctrlKey)) {
props.onSaveComment(editComment);
import TagsIcon from '../../../components/icons/TagsIcon';
import { translate, translateWithParameters } from '../../../helpers/l10n';
import { fileFromPath, limitComponentName } from '../../../helpers/path';
+import { ComponentQualifier } from '../../../types/component';
import { Issue } from '../../../types/types';
import SelectList from '../../common/SelectList';
import SelectListItem from '../../common/SelectListItem';
import StatusHelper from '../../shared/StatusHelper';
import Avatar from '../../ui/Avatar';
-interface Props {
+interface SimilarIssuesPopupProps {
issue: Issue;
onFilter: (property: string, issue: Issue) => void;
}
-export default class SimilarIssuesPopup extends React.PureComponent<Props> {
- handleSelect = (property: string) => {
- this.props.onFilter(property, this.props.issue);
- };
-
- render() {
- const { issue } = this.props;
-
- const items = [
- 'type',
- 'severity',
- 'status',
- 'resolution',
- 'assignee',
- 'rule',
- ...(issue.tags || []).map((tag) => `tag###${tag}`),
- 'project',
- 'file',
- ].filter((item) => item) as string[];
-
- const assignee = issue.assigneeName || issue.assignee;
-
- return (
- <DropdownOverlay noPadding={true}>
- <header className="menu-search">
- <h6>{translate('issue.filter_similar_issues')}</h6>
- </header>
-
- <SelectList
- className="issues-similar-issues-menu"
- currentItem={items[0]}
- items={items}
- onSelect={this.handleSelect}
- >
- <SelectListItem className="display-flex-center" item="type">
- <IssueTypeIcon className="little-spacer-right" query={issue.type} />
- {translate('issue.type', issue.type)}
+export default function SimilarIssuesPopup(props: SimilarIssuesPopupProps) {
+ const { issue } = props;
+
+ const items = [
+ 'type',
+ 'severity',
+ 'status',
+ 'resolution',
+ 'assignee',
+ 'rule',
+ ...(issue.tags ?? []).map((tag) => `tag###${tag}`),
+ 'project',
+ 'file',
+ ].filter((item) => item) as string[];
+
+ const assignee = issue.assigneeName ?? issue.assignee;
+
+ return (
+ <DropdownOverlay noPadding={true}>
+ <div className="menu-search">
+ <h6>{translate('issue.filter_similar_issues')}</h6>
+ </div>
+
+ <SelectList
+ className="issues-similar-issues-menu"
+ currentItem={items[0]}
+ items={items}
+ onSelect={(property: string) => {
+ props.onFilter(property, issue);
+ }}
+ >
+ <SelectListItem className="display-flex-center" item="type">
+ <IssueTypeIcon className="little-spacer-right" query={issue.type} />
+ {translate('issue.type', issue.type)}
+ </SelectListItem>
+
+ <SelectListItem item="severity">
+ <SeverityHelper className="display-flex-center" severity={issue.severity} />
+ </SelectListItem>
+
+ <SelectListItem item="status">
+ <StatusHelper
+ className="display-flex-center"
+ resolution={undefined}
+ status={issue.status}
+ />
+ </SelectListItem>
+
+ <SelectListItem item="resolution">
+ {issue.resolution != null
+ ? translate('issue.resolution', issue.resolution)
+ : translate('unresolved')}
+ </SelectListItem>
+
+ <SelectListItem item="assignee">
+ {assignee ? (
+ <span>
+ {translate('assigned_to')}
+ <Avatar
+ className="little-spacer-left little-spacer-right"
+ hash={issue.assigneeAvatar}
+ name={assignee}
+ size={16}
+ />
+ {issue.assigneeActive === false
+ ? translateWithParameters('user.x_deleted', assignee)
+ : assignee}
+ </span>
+ ) : (
+ translate('unassigned')
+ )}
+ </SelectListItem>
+
+ <li className="divider" />
+
+ <SelectListItem item="rule">{limitComponentName(issue.ruleName)}</SelectListItem>
+
+ {issue.tags?.map((tag) => (
+ <SelectListItem item={`tag###${tag}`} key={`tag###${tag}`}>
+ <TagsIcon className="little-spacer-right text-middle" />
+ <span className="text-middle">{tag}</span>
</SelectListItem>
-
- <SelectListItem item="severity">
- <SeverityHelper className="display-flex-center" severity={issue.severity} />
- </SelectListItem>
-
- <SelectListItem item="status">
- <StatusHelper
- className="display-flex-center"
- resolution={undefined}
- status={issue.status}
- />
- </SelectListItem>
-
- <SelectListItem item="resolution">
- {issue.resolution != null
- ? translate('issue.resolution', issue.resolution)
- : translate('unresolved')}
- </SelectListItem>
-
- <SelectListItem item="assignee">
- {assignee ? (
- <span>
- {translate('assigned_to')}
- <Avatar
- className="little-spacer-left little-spacer-right"
- hash={issue.assigneeAvatar}
- name={assignee}
- size={16}
- />
- {issue.assigneeActive === false
- ? translateWithParameters('user.x_deleted', assignee)
- : assignee}
- </span>
- ) : (
- translate('unassigned')
- )}
- </SelectListItem>
-
- <li className="divider" />
-
- <SelectListItem item="rule">{limitComponentName(issue.ruleName)}</SelectListItem>
-
- {issue.tags != null &&
- issue.tags.map((tag) => (
- <SelectListItem item={`tag###${tag}`} key={`tag###${tag}`}>
- <TagsIcon className="little-spacer-right text-middle" />
- <span className="text-middle">{tag}</span>
- </SelectListItem>
- ))}
-
- <li className="divider" />
-
- <SelectListItem item="project">
- <QualifierIcon className="little-spacer-right" qualifier="TRK" />
- {issue.projectName}
- </SelectListItem>
-
- <SelectListItem item="file">
- <QualifierIcon className="little-spacer-right" qualifier={issue.componentQualifier} />
- {fileFromPath(issue.componentLongName)}
- </SelectListItem>
- </SelectList>
- </DropdownOverlay>
- );
- }
+ ))}
+
+ <li className="divider" />
+
+ <SelectListItem item="project">
+ <QualifierIcon className="little-spacer-right" qualifier={ComponentQualifier.Project} />
+ {issue.projectName}
+ </SelectListItem>
+
+ <SelectListItem item="file">
+ <QualifierIcon className="little-spacer-right" qualifier={issue.componentQualifier} />
+ {fileFromPath(issue.componentLongName)}
+ </SelectListItem>
+ </SelectList>
+ </DropdownOverlay>
+ );
}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 * as React from 'react';
-import { getIssueChangelog } from '../../../../api/issues';
-import { waitAndUpdate } from '../../../../helpers/testUtils';
-import ChangelogPopup from '../ChangelogPopup';
-
-jest.mock('../../../../api/issues', () => ({
- getIssueChangelog: jest.fn().mockResolvedValue({
- changelog: [
- {
- creationDate: '2017-03-01T09:36:01+0100',
- user: 'john.doe',
- isUserActive: true,
- userName: 'John Doe',
- avatar: 'gravatarhash',
- diffs: [{ key: 'severity', newValue: 'MINOR', oldValue: 'CRITICAL' }],
- },
- ],
- }),
-}));
-
-beforeEach(() => {
- jest.clearAllMocks();
-});
-
-it('should render the changelog popup correctly', async () => {
- const wrapper = shallowRender();
- await waitAndUpdate(wrapper);
- expect(getIssueChangelog).toHaveBeenCalledWith('issuekey');
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should render the changelog popup when we have a deleted user', async () => {
- (getIssueChangelog as jest.Mock).mockResolvedValueOnce({
- changelog: [
- {
- creationDate: '2017-03-01T09:36:01+0100',
- user: 'john.doe',
- isUserActive: false,
- diffs: [{ key: 'severity', newValue: 'MINOR', oldValue: 'CRITICAL' }],
- },
- ],
- });
- const wrapper = shallowRender();
- await waitAndUpdate(wrapper);
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should render the changelog popup when change was triggered by a webhook with external user', async () => {
- (getIssueChangelog as jest.Mock).mockResolvedValueOnce({
- changelog: [
- {
- creationDate: '2017-03-01T09:36:01+0100',
- user: null,
- isUserActive: false,
- diffs: [{ key: 'severity', newValue: 'MINOR', oldValue: 'CRITICAL' }],
- webhookSource: 'GitHub',
- externalUser: 'toto@github.com',
- },
- ],
- });
- const wrapper = shallowRender();
- await waitAndUpdate(wrapper);
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should render the changelog popup when change was triggered by a webhook without user', async () => {
- (getIssueChangelog as jest.Mock).mockResolvedValueOnce({
- changelog: [
- {
- creationDate: '2017-03-01T09:36:01+0100',
- user: null,
- isUserActive: false,
- diffs: [{ key: 'severity', newValue: 'MINOR', oldValue: 'CRITICAL' }],
- webhookSource: 'GitHub',
- },
- ],
- });
- const wrapper = shallowRender();
- await waitAndUpdate(wrapper);
- expect(wrapper).toMatchSnapshot();
-});
-
-it('should render the changelog popup with SQ user when both SQ and external user are presents', async () => {
- (getIssueChangelog as jest.Mock).mockResolvedValueOnce({
- changelog: [
- {
- creationDate: '2017-03-01T09:36:01+0100',
- user: 'toto@sonarqube.com',
- isUserActive: false,
- diffs: [{ key: 'severity', newValue: 'MINOR', oldValue: 'CRITICAL' }],
- webhookSource: 'GitHub',
- externalUser: 'toto@github.com',
- },
- ],
- });
- const wrapper = shallowRender();
- await waitAndUpdate(wrapper);
- expect(wrapper).toMatchSnapshot();
-});
-
-function shallowRender(props: Partial<ChangelogPopup['props']> = {}) {
- return shallow(
- <ChangelogPopup
- issue={{
- key: 'issuekey',
- author: 'john.david.dalton@gmail.com',
- creationDate: '2017-03-01T09:36:01+0100',
- }}
- {...props}
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 * as React from 'react';
-import { click } from '../../../../helpers/testUtils';
-import CommentDeletePopup from '../CommentDeletePopup';
-
-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);
-});
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 { screen } from '@testing-library/react';
-import userEvent from '@testing-library/user-event';
-import * as React from 'react';
-import { renderComponent } from '../../../../helpers/testReactTestingUtils';
-import CommentPopup, { CommentPopupProps } from '../CommentPopup';
-
-it('should trigger comment change', async () => {
- const user = userEvent.setup();
- const onComment = jest.fn();
- const toggleComment = jest.fn();
- shallowRender({ onComment, toggleComment });
-
- expect(await screen.findByRole('textbox')).toHaveFocus();
- await user.keyboard('test');
- await user.keyboard('{Control>}{Enter}{/Control}');
- expect(onComment).toHaveBeenCalledWith('test');
-
- await user.click(screen.getByRole('button', { name: 'issue.comment.add_comment.cancel' }));
- expect(toggleComment).toHaveBeenCalledWith(false);
-});
-
-function shallowRender(overrides: Partial<CommentPopupProps> = {}) {
- return renderComponent(
- <CommentPopup
- onComment={jest.fn()}
- placeholder="placeholder test"
- toggleComment={jest.fn()}
- {...overrides}
- />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 * as React from 'react';
-import { searchUsers } from '../../../../api/users';
-import { mockLoggedInUser, mockUser } from '../../../../helpers/testMocks';
-import { waitAndUpdate } from '../../../../helpers/testUtils';
-import { SetAssigneePopup } from '../SetAssigneePopup';
-
-jest.mock('../../../../api/users', () => {
- const { mockUser } = jest.requireActual('../../../../helpers/testMocks');
- return { searchUsers: jest.fn().mockResolvedValue({ users: [mockUser()] }) };
-});
-
-beforeEach(() => {
- jest.clearAllMocks();
-});
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should allow to search for a user on SQ', async () => {
- const wrapper = shallowRender();
- wrapper.find('SearchBox').prop<Function>('onChange')('o');
- await waitAndUpdate(wrapper);
- expect(searchUsers).toHaveBeenCalledWith({ q: 'o', ps: 10 });
- expect(wrapper.state('users')).toEqual([mockUser()]);
-});
-
-function shallowRender(props: Partial<SetAssigneePopup['props']> = {}) {
- return shallow(
- <SetAssigneePopup currentUser={mockLoggedInUser()} onSelect={jest.fn()} {...props} />
- );
-}
+++ /dev/null
-/*
- * SonarQube
- * Copyright (C) 2009-2023 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 * as React from 'react';
-import { mockIssue } from '../../../../helpers/testMocks';
-import SimilarIssuesPopup from '../SimilarIssuesPopup';
-
-it('should render correctly', () => {
- expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should render correctly when assigned', () => {
- expect(
- shallowRender({
- issue: mockIssue(false, { assignee: 'luke', assigneeName: 'Luke Skywalker' }),
- }).find('SelectListItem[item="assignee"]')
- ).toMatchSnapshot();
-
- expect(
- shallowRender({ issue: mockIssue(false, { assignee: 'luke', assigneeActive: false }) }).find(
- 'SelectListItem[item="assignee"]'
- )
- ).toMatchSnapshot();
-});
-
-it('should filter properly', () => {
- const issue = mockIssue();
- const onFilter = jest.fn();
- const wrapper = shallowRender({ issue, onFilter });
- wrapper.find('SelectList').prop<Function>('onSelect')('assignee');
- expect(onFilter).toHaveBeenCalledWith('assignee', issue);
-});
-
-function shallowRender(props: Partial<SimilarIssuesPopup['props']> = {}) {
- return shallow(
- <SimilarIssuesPopup
- issue={mockIssue(false, { tags: ['test-tag'] })}
- onFilter={jest.fn()}
- {...props}
- />
- );
-}
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render the changelog popup correctly 1`] = `
-<DropdownOverlay
- placement="bottom-right"
->
- <div
- className="menu is-container issue-changelog"
- >
- <table
- className="spaced"
- >
- <tbody>
- <tr>
- <td
- className="thin text-left text-top nowrap"
- >
- <DateTimeFormatter
- date="2017-03-01T09:36:01+0100"
- />
- </td>
- <td
- className="text-left text-top"
- >
- created_by john.david.dalton@gmail.com
- </td>
- </tr>
- <tr
- key="0"
- >
- <td
- className="thin text-left text-top nowrap"
- >
- <DateTimeFormatter
- date="2017-03-01T09:36:01+0100"
- />
- </td>
- <td
- className="text-left text-top"
- >
- <p>
- <withAppStateContext(Avatar)
- className="little-spacer-right"
- hash="gravatarhash"
- name="John Doe"
- size={16}
- />
- John Doe
- </p>
- <IssueChangelogDiff
- diff={
- {
- "key": "severity",
- "newValue": "MINOR",
- "oldValue": "CRITICAL",
- }
- }
- key="severity"
- />
- </td>
- </tr>
- </tbody>
- </table>
- </div>
-</DropdownOverlay>
-`;
-
-exports[`should render the changelog popup when change was triggered by a webhook with external user 1`] = `
-<DropdownOverlay
- placement="bottom-right"
->
- <div
- className="menu is-container issue-changelog"
- >
- <table
- className="spaced"
- >
- <tbody>
- <tr>
- <td
- className="thin text-left text-top nowrap"
- >
- <DateTimeFormatter
- date="2017-03-01T09:36:01+0100"
- />
- </td>
- <td
- className="text-left text-top"
- >
- created_by john.david.dalton@gmail.com
- </td>
- </tr>
- <tr
- key="0"
- >
- <td
- className="thin text-left text-top nowrap"
- >
- <DateTimeFormatter
- date="2017-03-01T09:36:01+0100"
- />
- </td>
- <td
- className="text-left text-top"
- >
- <p>
- <withAppStateContext(Avatar)
- className="little-spacer-right"
- name="toto@github.com"
- size={16}
- />
- toto@github.com
- issue.changelog.webhook_source.GitHub
- </p>
- <IssueChangelogDiff
- diff={
- {
- "key": "severity",
- "newValue": "MINOR",
- "oldValue": "CRITICAL",
- }
- }
- key="severity"
- />
- </td>
- </tr>
- </tbody>
- </table>
- </div>
-</DropdownOverlay>
-`;
-
-exports[`should render the changelog popup when change was triggered by a webhook without user 1`] = `
-<DropdownOverlay
- placement="bottom-right"
->
- <div
- className="menu is-container issue-changelog"
- >
- <table
- className="spaced"
- >
- <tbody>
- <tr>
- <td
- className="thin text-left text-top nowrap"
- >
- <DateTimeFormatter
- date="2017-03-01T09:36:01+0100"
- />
- </td>
- <td
- className="text-left text-top"
- >
- created_by john.david.dalton@gmail.com
- </td>
- </tr>
- <tr
- key="0"
- >
- <td
- className="thin text-left text-top nowrap"
- >
- <DateTimeFormatter
- date="2017-03-01T09:36:01+0100"
- />
- </td>
- <td
- className="text-left text-top"
- >
- <p>
- issue.changelog.webhook_source.GitHub
- </p>
- <IssueChangelogDiff
- diff={
- {
- "key": "severity",
- "newValue": "MINOR",
- "oldValue": "CRITICAL",
- }
- }
- key="severity"
- />
- </td>
- </tr>
- </tbody>
- </table>
- </div>
-</DropdownOverlay>
-`;
-
-exports[`should render the changelog popup when we have a deleted user 1`] = `
-<DropdownOverlay
- placement="bottom-right"
->
- <div
- className="menu is-container issue-changelog"
- >
- <table
- className="spaced"
- >
- <tbody>
- <tr>
- <td
- className="thin text-left text-top nowrap"
- >
- <DateTimeFormatter
- date="2017-03-01T09:36:01+0100"
- />
- </td>
- <td
- className="text-left text-top"
- >
- created_by john.david.dalton@gmail.com
- </td>
- </tr>
- <tr
- key="0"
- >
- <td
- className="thin text-left text-top nowrap"
- >
- <DateTimeFormatter
- date="2017-03-01T09:36:01+0100"
- />
- </td>
- <td
- className="text-left text-top"
- >
- <p>
- <withAppStateContext(Avatar)
- className="little-spacer-right"
- name="john.doe"
- size={16}
- />
- user.x_deleted.john.doe
- </p>
- <IssueChangelogDiff
- diff={
- {
- "key": "severity",
- "newValue": "MINOR",
- "oldValue": "CRITICAL",
- }
- }
- key="severity"
- />
- </td>
- </tr>
- </tbody>
- </table>
- </div>
-</DropdownOverlay>
-`;
-
-exports[`should render the changelog popup with SQ user when both SQ and external user are presents 1`] = `
-<DropdownOverlay
- placement="bottom-right"
->
- <div
- className="menu is-container issue-changelog"
- >
- <table
- className="spaced"
- >
- <tbody>
- <tr>
- <td
- className="thin text-left text-top nowrap"
- >
- <DateTimeFormatter
- date="2017-03-01T09:36:01+0100"
- />
- </td>
- <td
- className="text-left text-top"
- >
- created_by john.david.dalton@gmail.com
- </td>
- </tr>
- <tr
- key="0"
- >
- <td
- className="thin text-left text-top nowrap"
- >
- <DateTimeFormatter
- date="2017-03-01T09:36:01+0100"
- />
- </td>
- <td
- className="text-left text-top"
- >
- <p>
- <withAppStateContext(Avatar)
- className="little-spacer-right"
- name="toto@sonarqube.com"
- size={16}
- />
- toto@sonarqube.com
- issue.changelog.webhook_source.GitHub
- </p>
- <IssueChangelogDiff
- diff={
- {
- "key": "severity",
- "newValue": "MINOR",
- "oldValue": "CRITICAL",
- }
- }
- key="severity"
- />
- </td>
- </tr>
- </tbody>
- </table>
- </div>
-</DropdownOverlay>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render the comment delete popup correctly 1`] = `
-<DropdownOverlay
- placement="bottom-right"
->
- <div
- className="menu is-container"
- >
- <div
- className="spacer-bottom"
- >
- issue.comment.delete_confirm_message
- </div>
- <Button
- className="button-red"
- onClick={[MockFunction]}
- >
- delete
- </Button>
- </div>
-</DropdownOverlay>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<DropdownOverlay
- noPadding={true}
->
- <div
- className="multi-select"
- >
- <div
- className="menu-search"
- >
- <SearchBox
- autoFocus={true}
- className="little-spacer-top"
- minLength={2}
- onChange={[Function]}
- placeholder="search.search_for_users"
- value=""
- />
- </div>
- <SelectList
- currentItem="luke"
- items={
- [
- "luke",
- "",
- ]
- }
- onSelect={[MockFunction]}
- >
- <SelectListItem
- item="luke"
- key="luke"
- >
- <withAppStateContext(Avatar)
- className="spacer-right"
- name="Skywalker"
- size={16}
- />
- <span
- className="text-middle"
- style={
- {
- "marginLeft": 24,
- }
- }
- >
- Skywalker
- </span>
- </SelectListItem>
- <SelectListItem
- item=""
- key=""
- >
- <span
- className="text-middle"
- style={
- {
- "marginLeft": undefined,
- }
- }
- >
- unassigned
- </span>
- </SelectListItem>
- </SelectList>
- </div>
-</DropdownOverlay>
-`;
+++ /dev/null
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<DropdownOverlay
- noPadding={true}
->
- <header
- className="menu-search"
- >
- <h6>
- issue.filter_similar_issues
- </h6>
- </header>
- <SelectList
- className="issues-similar-issues-menu"
- currentItem="type"
- items={
- [
- "type",
- "severity",
- "status",
- "resolution",
- "assignee",
- "rule",
- "tag###test-tag",
- "project",
- "file",
- ]
- }
- onSelect={[Function]}
- >
- <SelectListItem
- className="display-flex-center"
- item="type"
- >
- <IssueTypeIcon
- className="little-spacer-right"
- query="BUG"
- />
- issue.type.BUG
- </SelectListItem>
- <SelectListItem
- item="severity"
- >
- <SeverityHelper
- className="display-flex-center"
- severity="MAJOR"
- />
- </SelectListItem>
- <SelectListItem
- item="status"
- >
- <StatusHelper
- className="display-flex-center"
- status="OPEN"
- />
- </SelectListItem>
- <SelectListItem
- item="resolution"
- >
- unresolved
- </SelectListItem>
- <SelectListItem
- item="assignee"
- >
- unassigned
- </SelectListItem>
- <li
- className="divider"
- />
- <SelectListItem
- item="rule"
- >
- foo
- </SelectListItem>
- <SelectListItem
- item="tag###test-tag"
- key="tag###test-tag"
- >
- <TagsIcon
- className="little-spacer-right text-middle"
- />
- <span
- className="text-middle"
- >
- test-tag
- </span>
- </SelectListItem>
- <li
- className="divider"
- />
- <SelectListItem
- item="project"
- >
- <QualifierIcon
- className="little-spacer-right"
- qualifier="TRK"
- />
- Foo
- </SelectListItem>
- <SelectListItem
- item="file"
- >
- <QualifierIcon
- className="little-spacer-right"
- qualifier="FIL"
- />
- main.js
- </SelectListItem>
- </SelectList>
-</DropdownOverlay>
-`;
-
-exports[`should render correctly when assigned 1`] = `
-<SelectListItem
- item="assignee"
->
- <span>
- assigned_to
- <withAppStateContext(Avatar)
- className="little-spacer-left little-spacer-right"
- name="Luke Skywalker"
- size={16}
- />
- Luke Skywalker
- </span>
-</SelectListItem>
-`;
-
-exports[`should render correctly when assigned 2`] = `
-<SelectListItem
- item="assignee"
->
- <span>
- assigned_to
- <withAppStateContext(Avatar)
- className="little-spacer-left little-spacer-right"
- name="luke"
- size={16}
- />
- user.x_deleted.luke
- </span>
-</SelectListItem>
-`;
import { colors } from '../app/theme';
import { AlmKeys } from '../types/alm-settings';
import { ComponentQualifier } from '../types/component';
-import { IssueScope, IssueType } from '../types/issues';
+import { IssueScope, IssueSeverity, IssueType } from '../types/issues';
import { RuleType } from '../types/types';
-export const SEVERITIES = ['BLOCKER', 'CRITICAL', 'MAJOR', 'MINOR', 'INFO'];
+export const SEVERITIES = Object.values(IssueSeverity);
export const STATUSES = ['OPEN', 'REOPENED', 'CONFIRMED', 'RESOLVED', 'CLOSED'];
export const ISSUE_TYPES: IssueType[] = [
IssueType.Bug,
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+import { uniqueId } from 'lodash';
import { Query } from '../../apps/issues/utils';
import { ReferencedRule } from '../../types/issues';
-import { IssueChangelog } from '../../types/types';
+import { IssueChangelog, IssueChangelogDiff, IssueComment } from '../../types/types';
export function mockReferencedRule(overrides: Partial<ReferencedRule> = {}): ReferencedRule {
return {
isUserActive: true,
user: 'luke.skywalker',
userName: 'Luke Skywalker',
- diffs: [
- {
- key: 'assign',
- newValue: 'darth.vader',
- oldValue: 'luke.skywalker',
- },
- ],
+ diffs: [mockIssueChangelogDiff()],
+ ...overrides,
+ };
+}
+
+export function mockIssueChangelogDiff(
+ overrides: Partial<IssueChangelogDiff> = {}
+): IssueChangelogDiff {
+ return {
+ key: 'assign',
+ newValue: 'darth.vader',
+ oldValue: 'luke.skywalker',
+ ...overrides,
+ };
+}
+
+export function mockIssueComment(overrides: Partial<IssueComment> = {}): IssueComment {
+ return {
+ author: 'luke.skywalker',
+ authorLogin: 'luke.skywalker',
+ authorName: 'Luke Skywalker',
+ createdAt: '2018-10-01',
+ htmlText: 'This is a <strong>comment</strong>, <code>bud</code>',
+ key: uniqueId(),
+ markdown: 'This is a *comment*, `bud`',
+ updatable: false,
...overrides,
};
}
status: IssueStatus.Open,
textRange: { startLine: 25, endLine: 26, startOffset: 0, endOffset: 15 },
type: IssueType.CodeSmell,
+ transitions: [],
scope: IssueScope.Main,
...overrides,
};
SecurityHotspot = 'SECURITY_HOTSPOT',
}
+// Keep this enum in the correct order (most severe to least severe).
export enum IssueSeverity {
Blocker = 'BLOCKER',
- Minor = 'MINOR',
Critical = 'CRITICAL',
- Info = 'INFO',
Major = 'MAJOR',
+ Minor = 'MINOR',
+ Info = 'INFO',
}
export enum IssueScope {
Closed = 'CLOSED',
}
+export enum IssueActions {
+ SetType = 'set_type',
+ SetTags = 'set_tags',
+ SetSeverity = 'set_severity',
+ Comment = 'comment',
+ Assign = 'assign',
+}
+
+export enum IssueTransition {
+ Confirm = 'confirm',
+ UnConfirm = 'unconfirm',
+ Resolve = 'resolve',
+ FalsePositive = 'falsepositive',
+ WontFix = 'wontfix',
+ Reopen = 'reopen',
+}
+
interface Comment {
createdAt: string;
htmlText: string;
export interface RawIssue {
actions: string[];
- transitions?: string[];
+ transitions: string[];
tags?: string[];
assignee?: string;
author?: string;
- comments?: Array<Comment>;
+ comments?: Comment[];
creationDate: string;
component: string;
flows?: Array<{
components?: Array<{ key: string; name: string }>;
issue: RawIssue;
rules?: Array<{}>;
- users?: Array<UserBase>;
+ users?: UserBase[];
}
export interface RawIssuesResponse {
languages: ReferencedLanguage[];
paging: Paging;
rules?: Array<{}>;
- users?: Array<UserBase>;
+ users?: UserBase[];
}
export interface FetchIssuesPromise {
issue.assign.formlink=Assign
issue.assign.to_me=to me
issue.quick_fix_available_with_sonarlint=Quick fix available in {link}
+issue.quick_fix_available_with_sonarlint_no_link=Quick fix available in SonarLint
issue.comment.add_comment=Add Comment
issue.comment.add_comment.cancel=Cancel adding comment
issue.comment.enter_comment=Enter Comment
# ISSUE CHANGELOG
#
#------------------------------------------------------------------------------
+issue.changelog.found_on_x_show_more=Found on {0}; click to see changelog
issue.changelog.changed_to={0} changed to {1}
issue.changelog.was=was {0}
issue.changelog.webhook_source= (change triggered by a {0} webhook)