@@ -17,13 +17,13 @@ | |||
* 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, | |||
@@ -31,10 +31,12 @@ import { | |||
} from '../../helpers/testMocks'; | |||
import { | |||
ASSIGNEE_ME, | |||
IssueActions, | |||
IssueResolution, | |||
IssueScope, | |||
IssueSeverity, | |||
IssueStatus, | |||
IssueTransition, | |||
IssueType, | |||
RawFacet, | |||
RawIssue, | |||
@@ -49,12 +51,13 @@ import { | |||
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, | |||
@@ -97,11 +100,13 @@ interface IssueData { | |||
export default class IssuesServiceMock { | |||
isAdmin = false; | |||
currentUser: LoggedInUser; | |||
standards?: Standards; | |||
defaultList: IssueData[]; | |||
list: IssueData[]; | |||
constructor() { | |||
this.currentUser = mockLoggedInUser(); | |||
this.defaultList = [ | |||
{ | |||
issue: mockRawIssue(false, { | |||
@@ -336,7 +341,7 @@ export default class IssuesServiceMock { | |||
}, | |||
{ | |||
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', | |||
@@ -391,7 +396,7 @@ export default class IssuesServiceMock { | |||
}, | |||
{ | |||
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', | |||
@@ -444,19 +449,29 @@ export default class IssuesServiceMock { | |||
(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> { | |||
@@ -703,7 +718,7 @@ export default class IssuesServiceMock { | |||
}; | |||
handleGetCurrentUser = () => { | |||
return this.reply(mockCurrentUser()); | |||
return this.reply(this.currentUser); | |||
}; | |||
handleDismissNotification = (noticeType: NoticeType) => { | |||
@@ -723,27 +738,45 @@ export default class IssuesServiceMock { | |||
}; | |||
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( | |||
@@ -762,14 +795,13 @@ export default class IssuesServiceMock { | |||
}; | |||
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, | |||
@@ -781,31 +813,40 @@ export default class IssuesServiceMock { | |||
}; | |||
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 | |||
); | |||
}; | |||
@@ -817,6 +858,33 @@ export default class IssuesServiceMock { | |||
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); | |||
@@ -290,8 +290,10 @@ describe('issues app', () => { | |||
// 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(); | |||
@@ -481,7 +483,9 @@ describe('issues app', () => { | |||
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 | |||
@@ -503,7 +507,9 @@ describe('issues app', () => { | |||
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 | |||
@@ -895,7 +901,7 @@ describe('issues item', () => { | |||
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(); | |||
}); |
@@ -39,7 +39,8 @@ import { | |||
} 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'; | |||
@@ -199,7 +200,9 @@ export default class MeasuresOverlay extends React.PureComponent<Props, State> { | |||
<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> | |||
) | |||
)} | |||
@@ -207,14 +210,18 @@ export default class MeasuresOverlay extends React.PureComponent<Props, State> { | |||
)} | |||
{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 && ( | |||
@@ -225,7 +232,9 @@ export default class MeasuresOverlay extends React.PureComponent<Props, State> { | |||
<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> |
@@ -31,10 +31,6 @@ | |||
cursor: initial; | |||
} | |||
.issue.hotspot { | |||
background-color: var(--hotspotBgColor); | |||
} | |||
.issue.selected, | |||
.issue-message-box.selected { | |||
box-shadow: none; |
@@ -25,8 +25,8 @@ import { getKeyboardShortcutEnabled } from '../../helpers/preferences'; | |||
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; |
@@ -0,0 +1,554 @@ | |||
/* | |||
* 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' }), | |||
}); | |||
} |
@@ -1,62 +0,0 @@ | |||
/* | |||
* 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} | |||
/> | |||
); | |||
} |
@@ -1,224 +0,0 @@ | |||
// 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> | |||
`; |
@@ -1,62 +0,0 @@ | |||
// 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]} | |||
/> | |||
`; |
@@ -1,113 +0,0 @@ | |||
/* | |||
* 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} | |||
/> | |||
); | |||
} |
@@ -20,7 +20,12 @@ | |||
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'; | |||
@@ -90,13 +95,12 @@ export default class IssueActionsBar extends React.PureComponent<Props, State> { | |||
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')}> | |||
@@ -110,17 +114,15 @@ export default class IssueActionsBar extends React.PureComponent<Props, State> { | |||
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} | |||
@@ -139,7 +141,7 @@ export default class IssueActionsBar extends React.PureComponent<Props, State> { | |||
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)} |
@@ -18,53 +18,58 @@ | |||
* 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); |
@@ -22,18 +22,18 @@ import { translate, translateWithParameters } from '../../../helpers/l10n'; | |||
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> | |||
); | |||
@@ -42,8 +42,8 @@ export default function IssueChangelogDiff({ diff }: Props) { | |||
<p> | |||
{translateWithParameters( | |||
'issue.change.from_branch', | |||
diff.oldValue || '', | |||
diff.newValue || '' | |||
diff.oldValue ?? '', | |||
diff.newValue ?? '' | |||
)} | |||
</p> | |||
); | |||
@@ -53,13 +53,13 @@ export default function IssueChangelogDiff({ diff }: Props) { | |||
<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; |
@@ -85,7 +85,7 @@ export default class IssueCommentLine extends React.PureComponent<Props, State> | |||
? 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" | |||
@@ -145,7 +145,7 @@ export default class IssueCommentLine extends React.PureComponent<Props, State> | |||
</div> | |||
)} | |||
</div> | |||
</div> | |||
</li> | |||
); | |||
} | |||
} |
@@ -37,7 +37,7 @@ export default function IssueMessageTags(props: IssueMessageTagsProps) { | |||
const { engine, quickFixAvailable, ruleStatus } = props; | |||
const { externalRulesRepoNames } = React.useContext(WorkspaceContext); | |||
const ruleEngine = (engine && externalRulesRepoNames && externalRulesRepoNames[engine]) || engine; | |||
const ruleEngine = (engine && externalRulesRepoNames[engine]) || engine; | |||
return ( | |||
<> | |||
@@ -64,7 +64,7 @@ export default function IssueMessageTags(props: IssueMessageTagsProps) { | |||
<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> | |||
)} |
@@ -26,6 +26,8 @@ import { translate, translateWithParameters } from '../../../helpers/l10n'; | |||
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'; | |||
@@ -57,7 +59,7 @@ export default function IssueTitleBar(props: IssueTitleBarProps) { | |||
<Tooltip | |||
overlay={translateWithParameters( | |||
'issue.this_issue_involves_x_code_locations', | |||
formatMeasure(locationsCount, 'INT') | |||
formatMeasure(locationsCount, MetricType.Integer) | |||
)} | |||
> | |||
<LocationIndex>{locationsCount}</LocationIndex> | |||
@@ -70,7 +72,7 @@ export default function IssueTitleBar(props: IssueTitleBarProps) { | |||
...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 ( |
@@ -19,15 +19,15 @@ | |||
*/ | |||
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'; | |||
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; | |||
@@ -89,7 +89,6 @@ export default class IssueView extends React.PureComponent<Props> { | |||
const issueClass = classNames('issue', { | |||
'no-click': this.props.onClick === undefined, | |||
hotspot: issue.type === 'SECURITY_HOTSPOT', | |||
'issue-with-checkbox': hasCheckbox, | |||
selected: this.props.selected, | |||
}); | |||
@@ -103,7 +102,7 @@ export default class IssueView extends React.PureComponent<Props> { | |||
> | |||
{hasCheckbox && ( | |||
<Checkbox | |||
checked={checked || false} | |||
checked={checked ?? false} | |||
className="issue-checkbox-container" | |||
onCheck={this.handleCheck} | |||
label={translateWithParameters('issues.action_select.label', issue.message)} | |||
@@ -130,7 +129,7 @@ export default class IssueView extends React.PureComponent<Props> { | |||
togglePopup={this.props.togglePopup} | |||
/> | |||
{issue.comments && issue.comments.length > 0 && ( | |||
<div className="issue-comments"> | |||
<ul className="issue-comments" data-testid="issue-comments"> | |||
{issue.comments.map((comment) => ( | |||
<IssueCommentLine | |||
comment={comment} | |||
@@ -139,7 +138,7 @@ export default class IssueView extends React.PureComponent<Props> { | |||
onEdit={this.editComment} | |||
/> | |||
))} | |||
</div> | |||
</ul> | |||
)} | |||
</div> | |||
); |
@@ -1,124 +0,0 @@ | |||
/* | |||
* 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); | |||
}); | |||
}); |
@@ -1,66 +0,0 @@ | |||
/* | |||
* 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} | |||
/> | |||
); | |||
} |
@@ -1,55 +0,0 @@ | |||
/* | |||
* 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} | |||
/> | |||
); | |||
} |
@@ -17,67 +17,56 @@ | |||
* 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} />); | |||
} |
@@ -1,68 +0,0 @@ | |||
/* | |||
* 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} /> | |||
); | |||
} |
@@ -1,66 +0,0 @@ | |||
/* | |||
* 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} | |||
/> | |||
); | |||
} |
@@ -1,55 +0,0 @@ | |||
/* | |||
* 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} | |||
/> | |||
); | |||
} |
@@ -1,50 +0,0 @@ | |||
/* | |||
* 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} | |||
/> | |||
); | |||
} |
@@ -1,51 +0,0 @@ | |||
/* | |||
* 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} | |||
/> | |||
); | |||
} |
@@ -1,42 +0,0 @@ | |||
/* | |||
* 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} | |||
/> | |||
); | |||
} |
@@ -1,910 +0,0 @@ | |||
// 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> | |||
`; |
@@ -1,152 +0,0 @@ | |||
// 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> | |||
`; |
@@ -1,88 +0,0 @@ | |||
// 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> | |||
`; |
@@ -1,55 +0,0 @@ | |||
// 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> | |||
`; |
@@ -1,266 +0,0 @@ | |||
// 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> | |||
`; |
@@ -1,181 +0,0 @@ | |||
// 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> | |||
`; |
@@ -1,89 +0,0 @@ | |||
// 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" | |||
/> | |||
`; |
@@ -1,849 +0,0 @@ | |||
// 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> | |||
`; |
@@ -1,8 +0,0 @@ | |||
// 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" | |||
/> | |||
`; |
@@ -1,11 +0,0 @@ | |||
// 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> | |||
`; |
@@ -85,7 +85,7 @@ export default class ChangelogPopup extends React.PureComponent<Props, State> { | |||
<DateTimeFormatter date={item.creationDate} /> | |||
</td> | |||
<td className="text-left text-top"> | |||
<p> | |||
<div> | |||
{userName && ( | |||
<> | |||
<Avatar | |||
@@ -104,7 +104,7 @@ export default class ChangelogPopup extends React.PureComponent<Props, State> { | |||
'issue.changelog.webhook_source', | |||
item.webhookSource | |||
)} | |||
</p> | |||
</div> | |||
{item.diffs.map((diff) => ( | |||
<IssueChangelogDiff diff={diff} key={diff.key} /> | |||
))} |
@@ -44,9 +44,9 @@ export default function CommentForm(props: CommentFormProps) { | |||
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); |
@@ -24,6 +24,7 @@ import QualifierIcon from '../../../components/icons/QualifierIcon'; | |||
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'; | |||
@@ -31,112 +32,107 @@ import SeverityHelper from '../../shared/SeverityHelper'; | |||
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> | |||
); | |||
} |
@@ -1,132 +0,0 @@ | |||
/* | |||
* 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} | |||
/> | |||
); | |||
} |
@@ -1,31 +0,0 @@ | |||
/* | |||
* 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); | |||
}); |
@@ -1,50 +0,0 @@ | |||
/* | |||
* 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} | |||
/> | |||
); | |||
} |
@@ -1,52 +0,0 @@ | |||
/* | |||
* 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} /> | |||
); | |||
} |
@@ -1,59 +0,0 @@ | |||
/* | |||
* 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} | |||
/> | |||
); | |||
} |
@@ -1,319 +0,0 @@ | |||
// 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> | |||
`; |
@@ -1,23 +0,0 @@ | |||
// 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> | |||
`; |
@@ -1,70 +0,0 @@ | |||
// 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> | |||
`; |
@@ -1,144 +0,0 @@ | |||
// 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> | |||
`; |
@@ -20,10 +20,10 @@ | |||
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, |
@@ -17,9 +17,10 @@ | |||
* 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 { | |||
@@ -35,13 +36,32 @@ export function mockIssueChangelog(overrides: Partial<IssueChangelog> = {}): Iss | |||
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, | |||
}; | |||
} |
@@ -295,6 +295,7 @@ export function mockRawIssue(withLocations = false, overrides: Partial<RawIssue> | |||
status: IssueStatus.Open, | |||
textRange: { startLine: 25, endLine: 26, startOffset: 0, endOffset: 15 }, | |||
type: IssueType.CodeSmell, | |||
transitions: [], | |||
scope: IssueScope.Main, | |||
...overrides, | |||
}; |
@@ -29,12 +29,13 @@ export enum IssueType { | |||
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 { | |||
@@ -58,6 +59,23 @@ export enum IssueStatus { | |||
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; | |||
@@ -87,11 +105,11 @@ export interface RawFlowLocation { | |||
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<{ | |||
@@ -120,7 +138,7 @@ export interface IssueResponse { | |||
components?: Array<{ key: string; name: string }>; | |||
issue: RawIssue; | |||
rules?: Array<{}>; | |||
users?: Array<UserBase>; | |||
users?: UserBase[]; | |||
} | |||
export interface RawIssuesResponse { | |||
@@ -131,7 +149,7 @@ export interface RawIssuesResponse { | |||
languages: ReferencedLanguage[]; | |||
paging: Paging; | |||
rules?: Array<{}>; | |||
users?: Array<UserBase>; | |||
users?: UserBase[]; | |||
} | |||
export interface FetchIssuesPromise { |
@@ -846,6 +846,7 @@ issue.assign.unassigned_click_to_assign=Unassigned, click to assign issue | |||
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 | |||
@@ -969,6 +970,7 @@ issues.not_all_issue_show_why=You do not have access to all projects in this por | |||
# 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) |