@@ -31,6 +31,10 @@ import { | |||
} from '../../helpers/testMocks'; | |||
import { | |||
ASSIGNEE_ME, | |||
IssueResolution, | |||
IssueScope, | |||
IssueSeverity, | |||
IssueStatus, | |||
IssueType, | |||
RawFacet, | |||
RawIssue, | |||
@@ -103,6 +107,7 @@ export default class IssuesServiceMock { | |||
issue: mockRawIssue(false, { | |||
key: 'issue101', | |||
component: 'foo:test1.js', | |||
creationDate: '2023-01-05T09:36:01+0100', | |||
message: 'Issue with no location message', | |||
type: IssueType.Vulnerability, | |||
rule: 'simpleRuleId', | |||
@@ -140,6 +145,9 @@ export default class IssuesServiceMock { | |||
], | |||
}, | |||
], | |||
resolution: IssueResolution.WontFix, | |||
scope: IssueScope.Main, | |||
tags: ['tag0', 'tag1'], | |||
}), | |||
snippets: keyBy( | |||
[ | |||
@@ -163,6 +171,7 @@ export default class IssuesServiceMock { | |||
component: 'foo:test1.js', | |||
message: 'FlowIssue', | |||
type: IssueType.CodeSmell, | |||
severity: IssueSeverity.Minor, | |||
rule: 'simpleRuleId', | |||
textRange: { | |||
startLine: 10, | |||
@@ -233,6 +242,7 @@ export default class IssuesServiceMock { | |||
], | |||
}, | |||
], | |||
tags: ['tag1'], | |||
}), | |||
snippets: keyBy( | |||
[ | |||
@@ -256,10 +266,11 @@ export default class IssuesServiceMock { | |||
component: 'foo:test1.js', | |||
message: 'Issue on file', | |||
assignee: mockLoggedInUser().login, | |||
type: IssueType.Vulnerability, | |||
type: IssueType.CodeSmell, | |||
rule: 'simpleRuleId', | |||
textRange: undefined, | |||
line: undefined, | |||
scope: IssueScope.Test, | |||
}), | |||
snippets: {}, | |||
}, | |||
@@ -338,6 +349,8 @@ export default class IssuesServiceMock { | |||
endOffset: 1, | |||
}, | |||
ruleDescriptionContextKey: 'spring', | |||
resolution: IssueResolution.Unresolved, | |||
status: IssueStatus.Open, | |||
}), | |||
snippets: keyBy( | |||
[ | |||
@@ -362,6 +375,8 @@ export default class IssuesServiceMock { | |||
startOffset: 0, | |||
endOffset: 1, | |||
}, | |||
resolution: IssueResolution.Fixed, | |||
status: IssueStatus.Confirmed, | |||
}), | |||
snippets: keyBy( | |||
[ | |||
@@ -381,7 +396,7 @@ export default class IssuesServiceMock { | |||
key: 'issue4', | |||
component: 'foo:test2.js', | |||
message: 'Issue with tags', | |||
rule: 'external_eslint_repo:no-div-regex', | |||
rule: 'other', | |||
textRange: { | |||
startLine: 25, | |||
endLine: 25, | |||
@@ -391,6 +406,10 @@ export default class IssuesServiceMock { | |||
ruleDescriptionContextKey: 'spring', | |||
ruleStatus: 'DEPRECATED', | |||
quickFixAvailable: true, | |||
tags: ['unused'], | |||
project: 'org.project2', | |||
assignee: 'email1@sonarsource.com', | |||
author: 'email3@sonarsource.com', | |||
}), | |||
snippets: keyBy( | |||
[ | |||
@@ -536,11 +555,63 @@ export default class IssuesServiceMock { | |||
}); | |||
}; | |||
handleSearchIssues = (query: RequestData): Promise<RawIssuesResponse> => { | |||
const facets = (query.facets ?? '').split(',').map((name: string) => { | |||
mockFacetDetailResponse = (facetsQuery: string): RawFacet[] => { | |||
return facetsQuery.split(',').map((name: string): RawFacet => { | |||
if (name === 'owaspTop10-2021') { | |||
return this.owasp2021FacetList(); | |||
} | |||
if (name === 'tags') { | |||
return { | |||
property: name, | |||
values: [ | |||
{ | |||
val: 'unused', | |||
count: 12842, | |||
}, | |||
{ | |||
val: 'confusing', | |||
count: 124, | |||
}, | |||
], | |||
}; | |||
} | |||
if (name === 'projects') { | |||
return { | |||
property: name, | |||
values: [ | |||
{ val: 'org.project1', count: 14685 }, | |||
{ val: 'org.project2', count: 3890 }, | |||
], | |||
}; | |||
} | |||
if (name === 'assignees') { | |||
return { | |||
property: name, | |||
values: [ | |||
{ val: 'email1@sonarsource.com', count: 675 }, | |||
{ val: 'email2@sonarsource.com', count: 531 }, | |||
], | |||
}; | |||
} | |||
if (name === 'author') { | |||
return { | |||
property: name, | |||
values: [ | |||
{ val: 'email3@sonarsource.com', count: 421 }, | |||
{ val: 'email4@sonarsource.com', count: 123 }, | |||
], | |||
}; | |||
} | |||
if (name === 'rules') { | |||
return { | |||
property: name, | |||
values: [ | |||
{ val: 'simpleRuleId', count: 8816 }, | |||
{ val: 'advancedRuleId', count: 2060 }, | |||
{ val: 'other', count: 1324 }, | |||
], | |||
}; | |||
} | |||
if (name === 'languages') { | |||
return { | |||
property: name, | |||
@@ -561,6 +632,10 @@ export default class IssuesServiceMock { | |||
values: [], | |||
}; | |||
}); | |||
}; | |||
handleSearchIssues = (query: RequestData): Promise<RawIssuesResponse> => { | |||
const facets = this.mockFacetDetailResponse((query.facets ?? '') as string); | |||
// Filter list (only supports assignee, type and severity) | |||
const filteredList = this.list | |||
@@ -573,9 +648,33 @@ export default class IssuesServiceMock { | |||
} | |||
return query.assignees.split(',').includes(item.issue.assignee); | |||
}) | |||
.filter((item) => { | |||
if (!query.tags) { | |||
return true; | |||
} | |||
if (!item.issue.tags) { | |||
return false; | |||
} | |||
return item.issue.tags.some((tag) => query.tags?.split(',').includes(tag)); | |||
}) | |||
.filter( | |||
(item) => | |||
!query.createdBefore || new Date(item.issue.creationDate) <= new Date(query.createdBefore) | |||
) | |||
.filter( | |||
(item) => | |||
!query.createdAfter || new Date(item.issue.creationDate) >= new Date(query.createdAfter) | |||
) | |||
.filter((item) => !query.types || query.types.split(',').includes(item.issue.type)) | |||
.filter( | |||
(item) => !query.severities || query.severities.split(',').includes(item.issue.severity) | |||
) | |||
.filter((item) => !query.scopes || query.scopes.split(',').includes(item.issue.scope)) | |||
.filter((item) => !query.statuses || query.statuses.split(',').includes(item.issue.status)) | |||
.filter((item) => !query.projects || query.projects.split(',').includes(item.issue.project)) | |||
.filter((item) => !query.rules || query.rules.split(',').includes(item.issue.rule)) | |||
.filter( | |||
(item) => !query.resolutions || query.resolutions.split(',').includes(item.issue.resolution) | |||
); | |||
// Splice list items according to paging using a fixed page size | |||
@@ -589,12 +688,17 @@ export default class IssuesServiceMock { | |||
effortTotal: 199629, | |||
facets, | |||
issues: listItems.map((line) => line.issue), | |||
languages: [], | |||
languages: [{ name: 'java' }, { name: 'python' }, { name: 'ts' }], | |||
paging: mockPaging({ | |||
pageIndex, | |||
pageSize, | |||
total: filteredList.length, | |||
}), | |||
users: [ | |||
{ login: 'login0' }, | |||
{ login: 'login1', name: 'Login 1' }, | |||
{ login: 'login2', name: 'Login 2' }, | |||
], | |||
}); | |||
}; | |||
@@ -638,8 +742,8 @@ export default class IssuesServiceMock { | |||
}; | |||
const resolutionMap: Dict<string> = { | |||
wontfix: 'WONTFIX', | |||
falsepositive: 'FALSE-POSITIVE', | |||
wontfix: IssueResolution.WontFix, | |||
falsepositive: IssueResolution.FalsePositive, | |||
}; | |||
return this.getActionsResponse( |
@@ -64,7 +64,37 @@ const ui = { | |||
issueItem7: byRole('region', { name: 'Issue with tags' }), | |||
issueItem8: byRole('region', { name: 'Issue on page 2' }), | |||
clearIssueTypeFacet: byRole('button', { name: 'clear_x_filter.issues.facet.types' }), | |||
codeSmellIssueTypeFilter: byRole('checkbox', { name: 'issue.type.CODE_SMELL' }), | |||
vulnerabilityIssueTypeFilter: byRole('checkbox', { name: 'issue.type.VULNERABILITY' }), | |||
clearSeverityFacet: byRole('button', { name: 'clear_x_filter.issues.facet.severities' }), | |||
majorSeverityFilter: byRole('checkbox', { name: 'severity.MAJOR' }), | |||
scopeFacet: byRole('button', { name: 'issues.facet.scopes' }), | |||
clearScopeFacet: byRole('button', { name: 'clear_x_filter.issues.facet.scopes' }), | |||
mainScopeFilter: byRole('checkbox', { name: 'issue.scope.MAIN' }), | |||
resolutionFacet: byRole('button', { name: 'issues.facet.resolutions' }), | |||
clearResolutionFacet: byRole('button', { name: 'clear_x_filter.issues.facet.resolutions' }), | |||
fixedResolutionFilter: byRole('checkbox', { name: 'issue.resolution.FIXED' }), | |||
statusFacet: byRole('button', { name: 'issues.facet.statuses' }), | |||
creationDateFacet: byRole('button', { name: 'issues.facet.createdAt' }), | |||
clearCreationDateFacet: byRole('button', { name: 'clear_x_filter.issues.facet.createdAt' }), | |||
clearStatusFacet: byRole('button', { name: 'clear_x_filter.issues.facet.statuses' }), | |||
openStatusFilter: byRole('checkbox', { name: 'issue.status.OPEN' }), | |||
confirmedStatusFilter: byRole('checkbox', { name: 'issue.status.CONFIRMED' }), | |||
ruleFacet: byRole('button', { name: 'issues.facet.rules' }), | |||
clearRuleFacet: byRole('button', { name: 'clear_x_filter.issues.facet.rules' }), | |||
tagFacet: byRole('button', { name: 'issues.facet.tags' }), | |||
clearTagFacet: byRole('button', { name: 'clear_x_filter.issues.facet.tags' }), | |||
projectFacet: byRole('button', { name: 'issues.facet.projects' }), | |||
clearProjectFacet: byRole('button', { name: 'clear_x_filter.issues.facet.projects' }), | |||
assigneeFacet: byRole('button', { name: 'issues.facet.assignees' }), | |||
clearAssigneeFacet: byRole('button', { name: 'clear_x_filter.issues.facet.assignees' }), | |||
authorFacet: byRole('button', { name: 'issues.facet.authors' }), | |||
clearAuthorFacet: byRole('button', { name: 'clear_x_filter.issues.facet.authors' }), | |||
dateInputMonthSelect: byRole('combobox', { name: 'Month:' }), | |||
dateInputYearSelect: byRole('combobox', { name: 'Year:' }), | |||
clearAllFilters: byRole('button', { name: 'clear_all_filters' }), | |||
}; | |||
@@ -305,45 +335,170 @@ describe('issues app', () => { | |||
}); | |||
}); | |||
describe('filtering', () => { | |||
it('should allow to reset all facets', async () => { | |||
it('should handle filtering from a specific issue properly', async () => { | |||
const user = userEvent.setup(); | |||
renderIssueApp(); | |||
await waitOnDataLoaded(); | |||
// Ensure issue type filter is unchecked | |||
expect(ui.codeSmellIssueTypeFilter.get()).not.toBeChecked(); | |||
expect(ui.vulnerabilityIssueTypeFilter.get()).not.toBeChecked(); | |||
expect(ui.issueItem1.get()).toBeInTheDocument(); | |||
expect(ui.issueItem2.get()).toBeInTheDocument(); | |||
// Open filter similar issue dropdown for issue 2 (Code smell) | |||
await user.click( | |||
await within(ui.issueItem2.get()).findByRole('button', { | |||
name: 'issue.filter_similar_issues', | |||
}) | |||
); | |||
await user.click( | |||
await within(ui.issueItem2.get()).findByRole('button', { | |||
name: 'issue.type.CODE_SMELL', | |||
}) | |||
); | |||
await user.click(ui.codeSmellIssueTypeFilter.get()); | |||
expect(ui.codeSmellIssueTypeFilter.get()).toBeChecked(); | |||
expect(ui.issueItem4.query()).not.toBeInTheDocument(); | |||
expect(ui.vulnerabilityIssueTypeFilter.get()).not.toBeChecked(); | |||
expect(ui.issueItem1.query()).not.toBeInTheDocument(); | |||
expect(ui.issueItem2.get()).toBeInTheDocument(); | |||
expect( | |||
screen.queryByRole('button', { name: 'issues.facet.owaspTop10_2021' }) | |||
).not.toBeInTheDocument(); | |||
// Clear filters | |||
await user.click(ui.clearAllFilters.get()); | |||
// Open filter similar issue dropdown for issue 3 (Vulnerability) | |||
await user.click( | |||
await within(await ui.issueItem1.find()).findByRole('button', { | |||
name: 'issue.filter_similar_issues', | |||
}) | |||
); | |||
await user.click( | |||
await within(await ui.issueItem1.find()).findByRole('button', { | |||
name: 'issue.type.VULNERABILITY', | |||
}) | |||
); | |||
expect(ui.codeSmellIssueTypeFilter.get()).not.toBeChecked(); | |||
expect(ui.issueItem4.get()).toBeInTheDocument(); | |||
expect(ui.vulnerabilityIssueTypeFilter.get()).toBeChecked(); | |||
expect(ui.issueItem1.get()).toBeInTheDocument(); | |||
expect(ui.issueItem2.query()).not.toBeInTheDocument(); | |||
// Standards should now be expanded and Owasp should be visible | |||
expect(screen.getByRole('button', { name: 'issues.facet.owaspTop10_2021' })).toBeVisible(); | |||
}); | |||
it('should handle filtering from a specific issue properly', async () => { | |||
it('should combine sidebar filters properly', async () => { | |||
const user = userEvent.setup(); | |||
renderIssueApp(); | |||
await waitOnDataLoaded(); | |||
// Get first issue list item | |||
const issueItem = await ui.issueItem2.find(); | |||
// Select only code smells (should make the first issue disappear) | |||
await user.click(ui.codeSmellIssueTypeFilter.get()); | |||
// Ensure issue type filter is unchecked | |||
expect(ui.codeSmellIssueTypeFilter.get()).not.toBeChecked(); | |||
// Select code smells + major severity | |||
await user.click(ui.majorSeverityFilter.get()); | |||
// Expand scope and set code smells + major severity + main scope | |||
await user.click(ui.scopeFacet.get()); | |||
await user.click(ui.mainScopeFilter.get()); | |||
// Resolution | |||
await user.click(ui.resolutionFacet.get()); | |||
await user.click(ui.fixedResolutionFilter.get()); | |||
// Stop to check that filters were applied as expected | |||
expect(ui.issueItem1.query()).not.toBeInTheDocument(); | |||
expect(ui.issueItem2.query()).not.toBeInTheDocument(); | |||
expect(ui.issueItem3.query()).not.toBeInTheDocument(); | |||
expect(ui.issueItem4.query()).not.toBeInTheDocument(); | |||
expect(ui.issueItem5.query()).not.toBeInTheDocument(); | |||
expect(ui.issueItem6.get()).toBeInTheDocument(); | |||
expect(ui.issueItem7.query()).not.toBeInTheDocument(); | |||
// Status | |||
await user.click(ui.statusFacet.get()); | |||
await user.click(ui.openStatusFilter.get()); | |||
expect(ui.issueItem6.query()).not.toBeInTheDocument(); // Issue 6 should vanish | |||
// Ctrl+click on confirmed status | |||
await user.keyboard('{Control>}'); | |||
await user.click(ui.confirmedStatusFilter.get()); | |||
await user.keyboard('{/Control}'); | |||
expect(ui.issueItem6.get()).toBeInTheDocument(); // Issue 6 should come back | |||
// Clear resolution filter | |||
await user.click(ui.clearResolutionFacet.get()); | |||
// Rule | |||
await user.click(ui.ruleFacet.get()); | |||
await user.click(screen.getByRole('checkbox', { name: 'other' })); | |||
// Tag | |||
await user.click(ui.tagFacet.get()); | |||
await user.click(screen.getByRole('checkbox', { name: 'unused' })); | |||
// Project | |||
await user.click(ui.projectFacet.get()); | |||
await user.click(screen.getByRole('checkbox', { name: 'org.project2' })); | |||
// Assignee | |||
await user.click(ui.assigneeFacet.get()); | |||
await user.click(screen.getByRole('checkbox', { name: 'email2@sonarsource.com' })); | |||
await user.click(screen.getByRole('checkbox', { name: 'email1@sonarsource.com' })); // Change assignee | |||
// Author | |||
await user.click(ui.authorFacet.get()); | |||
await user.click(screen.getByRole('checkbox', { name: 'email4@sonarsource.com' })); | |||
await user.click(screen.getByRole('checkbox', { name: 'email3@sonarsource.com' })); // Change author | |||
expect(ui.issueItem1.query()).not.toBeInTheDocument(); | |||
expect(ui.issueItem2.query()).not.toBeInTheDocument(); | |||
expect(ui.issueItem3.query()).not.toBeInTheDocument(); | |||
expect(ui.issueItem4.query()).not.toBeInTheDocument(); | |||
expect(ui.issueItem5.query()).not.toBeInTheDocument(); | |||
expect(ui.issueItem6.query()).not.toBeInTheDocument(); | |||
expect(ui.issueItem7.get()).toBeInTheDocument(); | |||
// Clear filters one by one | |||
await user.click(ui.clearIssueTypeFacet.get()); | |||
await user.click(ui.clearSeverityFacet.get()); | |||
await user.click(ui.clearScopeFacet.get()); | |||
await user.click(ui.clearStatusFacet.get()); | |||
await user.click(ui.clearRuleFacet.get()); | |||
await user.click(ui.clearTagFacet.get()); | |||
await user.click(ui.clearProjectFacet.get()); | |||
await user.click(ui.clearAssigneeFacet.get()); | |||
await user.click(ui.clearAuthorFacet.get()); | |||
expect(ui.issueItem1.get()).toBeInTheDocument(); | |||
expect(ui.issueItem2.get()).toBeInTheDocument(); | |||
expect(ui.issueItem3.get()).toBeInTheDocument(); | |||
expect(ui.issueItem4.get()).toBeInTheDocument(); | |||
expect(ui.issueItem5.get()).toBeInTheDocument(); | |||
expect(ui.issueItem6.get()).toBeInTheDocument(); | |||
expect(ui.issueItem7.get()).toBeInTheDocument(); | |||
}); | |||
// Open filter similar issue dropdown | |||
await user.click( | |||
await within(issueItem).findByRole('button', { name: 'issue.filter_similar_issues' }) | |||
); | |||
it('should allow to set creation date', async () => { | |||
const user = userEvent.setup(); | |||
renderIssueApp(mockLoggedInUser()); | |||
await waitOnDataLoaded(); | |||
// Select type | |||
await user.click( | |||
await within(issueItem).findByRole('button', { name: 'issue.type.CODE_SMELL' }) | |||
); | |||
// Select a specific date range such that only one issue matches | |||
await user.click(ui.creationDateFacet.get()); | |||
await user.click(screen.getByPlaceholderText('start_date')); | |||
await user.selectOptions(ui.dateInputMonthSelect.get(), 'January'); | |||
await user.selectOptions(ui.dateInputYearSelect.get(), '2023'); | |||
await user.click(screen.getByText('1')); | |||
await user.click(screen.getByText('10')); | |||
// Ensure issue type filter is now checked | |||
expect(ui.codeSmellIssueTypeFilter.get()).toBeChecked(); | |||
expect(ui.issueItem2.get()).toBeInTheDocument(); | |||
expect(ui.issueItem1.get()).toBeInTheDocument(); | |||
expect(ui.issueItem2.query()).not.toBeInTheDocument(); | |||
expect(ui.issueItem3.query()).not.toBeInTheDocument(); | |||
expect(ui.issueItem4.query()).not.toBeInTheDocument(); | |||
expect(ui.issueItem5.query()).not.toBeInTheDocument(); | |||
expect(ui.issueItem6.query()).not.toBeInTheDocument(); | |||
expect(ui.issueItem7.query()).not.toBeInTheDocument(); | |||
}); | |||
it('should allow to only show my issues', async () => { | |||
@@ -449,7 +604,7 @@ describe('issues item', () => { | |||
issuesHandler.setIsAdmin(true); | |||
renderIssueApp(); | |||
// Get 'Fix that' issue list item | |||
// Get a specific issue list item | |||
const listItem = within(await screen.findByRole('region', { name: 'Fix that' })); | |||
// Change issue type |
@@ -106,6 +106,7 @@ exports[`should render correctly 2`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [ | |||
{ | |||
"component": "main.js", | |||
@@ -264,6 +265,7 @@ exports[`should render correctly: no component found 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [ | |||
{ | |||
"component": "main.js", | |||
@@ -393,6 +395,7 @@ exports[`should render correctly: no component found 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [ | |||
{ | |||
"component": "main.js", |
@@ -25,6 +25,7 @@ import FacetItem from '../../../components/facet/FacetItem'; | |||
import FacetItemsList from '../../../components/facet/FacetItemsList'; | |||
import MultipleSelectionHint from '../../../components/facet/MultipleSelectionHint'; | |||
import { translate } from '../../../helpers/l10n'; | |||
import { IssueResolution } from '../../../types/issues'; | |||
import { Dict } from '../../../types/types'; | |||
import { formatFacetStat, Query } from '../utils'; | |||
@@ -38,7 +39,13 @@ interface Props { | |||
stats: Dict<number> | undefined; | |||
} | |||
const RESOLUTIONS = ['', 'FALSE-POSITIVE', 'FIXED', 'REMOVED', 'WONTFIX']; | |||
const RESOLUTIONS = [ | |||
IssueResolution.Unresolved, | |||
IssueResolution.FalsePositive, | |||
IssueResolution.Fixed, | |||
IssueResolution.Removed, | |||
IssueResolution.WontFix, | |||
]; | |||
export default class ResolutionFacet extends React.PureComponent<Props> { | |||
property = 'resolutions'; |
@@ -1,95 +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 { Query } from '../../utils'; | |||
import AssigneeFacet from '../AssigneeFacet'; | |||
it('should render', () => { | |||
expect(shallowRender({ assignees: ['foo'] })).toMatchSnapshot(); | |||
}); | |||
it('should select unassigned', () => { | |||
expect(shallowRender({ assigned: false }).find('ListStyleFacet').prop('values')).toEqual(['']); | |||
}); | |||
it('should call onChange', () => { | |||
const onChange = jest.fn(); | |||
const wrapper = shallowRender({ assignees: ['foo'], onChange }); | |||
const itemOnClick = wrapper.find('ListStyleFacet').prop<Function>('onItemClick'); | |||
itemOnClick(''); | |||
expect(onChange).toHaveBeenLastCalledWith({ assigned: false, assignees: [] }); | |||
itemOnClick('bar'); | |||
expect(onChange).toHaveBeenLastCalledWith({ assigned: true, assignees: ['bar'] }); | |||
itemOnClick('baz', true); | |||
expect(onChange).toHaveBeenLastCalledWith({ assigned: true, assignees: ['baz', 'foo'] }); | |||
}); | |||
describe('test behavior', () => { | |||
const instance = shallowRender({ | |||
assignees: ['foo', 'baz'], | |||
referencedUsers: { | |||
foo: { active: false, login: 'foo' }, | |||
baz: { active: true, login: 'baz', name: 'Name Baz' }, | |||
}, | |||
}).instance(); | |||
it('should correctly render assignee name', () => { | |||
expect(instance.getAssigneeName('')).toBe('unassigned'); | |||
expect(instance.getAssigneeName('bar')).toBe('bar'); | |||
expect(instance.getAssigneeName('baz')).toBe('Name Baz'); | |||
expect(instance.getAssigneeName('foo')).toBe('user.x_deleted.foo'); | |||
}); | |||
it('should correctly render facet item', () => { | |||
expect(instance.renderFacetItem('')).toBe('unassigned'); | |||
expect(instance.renderFacetItem('bar')).toBe('bar'); | |||
expect(instance.renderFacetItem('baz')).toMatchSnapshot(); | |||
expect(instance.renderFacetItem('foo')).toMatchSnapshot(); | |||
}); | |||
it('should correctly render search result correctly', () => { | |||
expect( | |||
instance.renderSearchResult({ active: true, login: 'bar', name: 'Name Bar' }, 'ba') | |||
).toMatchSnapshot(); | |||
expect(instance.renderSearchResult({ active: false, login: 'foo' }, 'fo')).toMatchSnapshot(); | |||
}); | |||
}); | |||
function shallowRender(props?: Partial<AssigneeFacet['props']>) { | |||
return shallow<AssigneeFacet>( | |||
<AssigneeFacet | |||
assigned={true} | |||
assignees={[]} | |||
fetching={false} | |||
loadSearchResultCount={jest.fn()} | |||
onChange={jest.fn()} | |||
onToggle={jest.fn()} | |||
open={true} | |||
query={{} as Query} | |||
referencedUsers={{ foo: { avatar: 'avatart-foo', login: 'name-foo', name: 'Name Foo' } }} | |||
stats={{ '': 5, foo: 13, bar: 7, baz: 6 }} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,57 +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 ListStyleFacet from '../../../../components/facet/ListStyleFacet'; | |||
import { mockComponent } from '../../../../helpers/mocks/component'; | |||
import { Query } from '../../utils'; | |||
import AuthorFacet from '../AuthorFacet'; | |||
it('should render correctly', () => { | |||
const wrapper = shallowRender(); | |||
expect(wrapper).toMatchSnapshot(); | |||
}); | |||
it('should notify of search result count correctly', () => { | |||
const loadSearchResultCount = jest.fn(); | |||
const wrapper = shallowRender({ loadSearchResultCount }); | |||
wrapper.find(ListStyleFacet).props().loadSearchResultCount!(['1', '2']); | |||
expect(loadSearchResultCount).toHaveBeenCalled(); | |||
}); | |||
function shallowRender(props: Partial<AuthorFacet['props']> = {}) { | |||
return shallow<AuthorFacet>( | |||
<AuthorFacet | |||
component={mockComponent()} | |||
fetching={false} | |||
loadSearchResultCount={jest.fn()} | |||
onChange={jest.fn()} | |||
onToggle={jest.fn()} | |||
open={true} | |||
query={{} as Query} | |||
stats={{}} | |||
author={[]} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,80 +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 { IntlShape } from 'react-intl'; | |||
import { mockComponent } from '../../../../helpers/mocks/component'; | |||
import { ComponentQualifier } from '../../../../types/component'; | |||
import { CreationDateFacet } from '../CreationDateFacet'; | |||
it('should render correctly', () => { | |||
expect(shallowRender({ open: false })).toMatchSnapshot('closed'); | |||
expect(shallowRender()).toMatchSnapshot('clear'); | |||
expect(shallowRender({ createdAt: '2019.05.21T13:33:00Z' })).toMatchSnapshot('created at'); | |||
expect( | |||
shallowRender({ | |||
createdAfter: new Date('2019.04.29T13:33:00Z'), | |||
createdAfterIncludesTime: true, | |||
}) | |||
).toMatchSnapshot('created after'); | |||
expect( | |||
shallowRender({ | |||
createdAfter: new Date('2019.04.29T13:33:00Z'), | |||
createdAfterIncludesTime: true, | |||
}) | |||
).toMatchSnapshot('created after timestamp'); | |||
expect(shallowRender({ component: mockComponent() })).toMatchSnapshot('project'); | |||
expect( | |||
shallowRender({ component: mockComponent({ qualifier: ComponentQualifier.Portfolio }) }) | |||
).toMatchSnapshot('portfolio'); | |||
}); | |||
it.each([ | |||
['week', '1w'], | |||
['month', '1m'], | |||
['year', '1y'], | |||
])('should render correctly for createdInLast %s', (_, createdInLast) => { | |||
expect(shallowRender({ component: mockComponent(), createdInLast })).toMatchSnapshot(); | |||
}); | |||
function shallowRender(props?: Partial<CreationDateFacet['props']>) { | |||
return shallow<CreationDateFacet>( | |||
<CreationDateFacet | |||
component={undefined} | |||
fetching={false} | |||
createdAfter={undefined} | |||
createdAfterIncludesTime={false} | |||
createdAt="" | |||
createdBefore={undefined} | |||
createdInLast="" | |||
inNewCodePeriod={false} | |||
intl={ | |||
{ | |||
formatDate: (date: string) => 'formatted.' + date, | |||
} as IntlShape | |||
} | |||
onChange={jest.fn()} | |||
onToggle={jest.fn()} | |||
open={true} | |||
stats={undefined} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,100 +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 { getDirectories } from '../../../../api/components'; | |||
import ListStyleFacet from '../../../../components/facet/ListStyleFacet'; | |||
import { mockBranch } from '../../../../helpers/mocks/branch-like'; | |||
import { mockComponent } from '../../../../helpers/mocks/component'; | |||
import { TreeComponentWithPath } from '../../../../types/component'; | |||
import { Query } from '../../utils'; | |||
import DirectoryFacet from '../DirectoryFacet'; | |||
jest.mock('../../../../api/components', () => ({ | |||
getDirectories: jest.fn().mockResolvedValue({ components: [] }), | |||
})); | |||
beforeEach(() => jest.clearAllMocks()); | |||
const branch = mockBranch(); | |||
const component = mockComponent(); | |||
it('should render correctly', () => { | |||
const wrapper = shallowRender(); | |||
const instance = wrapper.instance(); | |||
expect(wrapper).toMatchSnapshot(); | |||
expect( | |||
instance.renderSearchResult({ path: 'foo/bar' } as TreeComponentWithPath, 'foo') | |||
).toMatchSnapshot(); | |||
expect(instance.renderFacetItem('foo/bar')).toMatchSnapshot(); | |||
}); | |||
it('should properly search for directory', () => { | |||
const wrapper = shallowRender(); | |||
const query = 'foo'; | |||
wrapper.find(ListStyleFacet).props().onSearch(query); | |||
expect(getDirectories).toHaveBeenCalledWith({ | |||
branch: branch.name, | |||
component: component.key, | |||
q: query, | |||
ps: 30, | |||
p: undefined, | |||
}); | |||
}); | |||
describe("ListStyleFacet's callback props", () => { | |||
const wrapper = shallowRender(); | |||
const instance = wrapper.instance(); | |||
it('#getSearchResultText()', () => { | |||
expect(instance.getSearchResultText({ path: 'bar' } as TreeComponentWithPath)).toBe('bar'); | |||
}); | |||
it('#getSearchResultKey()', () => { | |||
expect(instance.getSearchResultKey({ path: 'foo/bar' } as TreeComponentWithPath)).toBe( | |||
'foo/bar' | |||
); | |||
}); | |||
it('#getFacetItemText()', () => { | |||
expect(instance.getFacetItemText('foo/bar')).toBe('foo/bar'); | |||
}); | |||
}); | |||
function shallowRender(props: Partial<DirectoryFacet['props']> = {}) { | |||
return shallow<DirectoryFacet>( | |||
<DirectoryFacet | |||
branchLike={branch} | |||
componentKey={component.key} | |||
directories={['foo/', 'bar/baz/']} | |||
fetching={false} | |||
loadSearchResultCount={jest.fn()} | |||
onChange={jest.fn()} | |||
onToggle={jest.fn()} | |||
open={false} | |||
query={{} as Query} | |||
stats={undefined} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,104 +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 { getFiles } from '../../../../api/components'; | |||
import ListStyleFacet from '../../../../components/facet/ListStyleFacet'; | |||
import { mockBranch } from '../../../../helpers/mocks/branch-like'; | |||
import { mockComponent } from '../../../../helpers/mocks/component'; | |||
import { TreeComponentWithPath } from '../../../../types/component'; | |||
import { Query } from '../../utils'; | |||
import FileFacet from '../FileFacet'; | |||
jest.mock('../../../../api/components', () => ({ | |||
getFiles: jest.fn().mockResolvedValue({ components: [] }), | |||
})); | |||
beforeEach(() => jest.clearAllMocks()); | |||
const branch = mockBranch(); | |||
const component = mockComponent(); | |||
const PATH = 'foo/bar.js'; | |||
it('should render correctly', () => { | |||
const wrapper = shallowRender(); | |||
const instance = wrapper.instance(); | |||
expect(wrapper).toMatchSnapshot(); | |||
expect( | |||
instance.renderSearchResult({ path: PATH } as TreeComponentWithPath, 'bar') | |||
).toMatchSnapshot(); | |||
expect(instance.renderFacetItem('fooUuid')).toMatchSnapshot(); | |||
}); | |||
it('should properly search for file', () => { | |||
const wrapper = shallowRender(); | |||
const query = 'foo'; | |||
wrapper.find(ListStyleFacet).props().onSearch(query); | |||
expect(getFiles).toHaveBeenCalledWith({ | |||
branch: branch.name, | |||
component: component.key, | |||
q: query, | |||
ps: 30, | |||
p: undefined, | |||
}); | |||
}); | |||
describe("ListStyleFacet's callback props", () => { | |||
const wrapper = shallowRender(); | |||
const instance = wrapper.instance(); | |||
it('#getSearchResultText()', () => { | |||
expect(instance.getSearchResultText({ path: PATH } as TreeComponentWithPath)).toBe( | |||
'foo/bar.js' | |||
); | |||
}); | |||
it('#getSearchResultKey()', () => { | |||
expect(instance.getSearchResultKey({ key: 'bar', path: 'bar' } as TreeComponentWithPath)).toBe( | |||
'bar' | |||
); | |||
}); | |||
it('#getFacetItemText()', () => { | |||
expect(instance.getFacetItemText('bar')).toBe('bar'); | |||
}); | |||
}); | |||
function shallowRender(props: Partial<FileFacet['props']> = {}) { | |||
return shallow<FileFacet>( | |||
<FileFacet | |||
branchLike={branch} | |||
componentKey={component.key} | |||
fetching={false} | |||
files={['foo', 'bar']} | |||
loadSearchResultCount={jest.fn()} | |||
onChange={jest.fn()} | |||
onToggle={jest.fn()} | |||
open={false} | |||
query={{} as Query} | |||
stats={undefined} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,53 +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 { render, screen } from '@testing-library/react'; | |||
import userEvent from '@testing-library/user-event'; | |||
import * as React from 'react'; | |||
import PeriodFilter, { PeriodFilterProps } from '../PeriodFilter'; | |||
it('should filter when clicked', async () => { | |||
const user = userEvent.setup(); | |||
const onChange = jest.fn(); | |||
renderPeriodFilter({ onChange }); | |||
await user.click(screen.getByText('issues.new_code')); | |||
expect(onChange).toHaveBeenCalledWith({ | |||
createdAfter: undefined, | |||
createdAt: undefined, | |||
createdBefore: undefined, | |||
createdInLast: undefined, | |||
inNewCodePeriod: true, | |||
}); | |||
}); | |||
function renderPeriodFilter(overrides: Partial<PeriodFilterProps> = {}) { | |||
return render( | |||
<PeriodFilter | |||
fetching={false} | |||
newCodeSelected={false} | |||
onChange={jest.fn()} | |||
stats={{}} | |||
{...overrides} | |||
/> | |||
); | |||
} |
@@ -1,117 +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 { keyBy } from 'lodash'; | |||
import * as React from 'react'; | |||
import { getTree, searchProjects } from '../../../../api/components'; | |||
import { mockComponent } from '../../../../helpers/mocks/component'; | |||
import { ComponentQualifier } from '../../../../types/component'; | |||
import { ReferencedComponent } from '../../../../types/issues'; | |||
import { Query } from '../../utils'; | |||
import ProjectFacet from '../ProjectFacet'; | |||
jest.mock('../../../../api/components', () => ({ | |||
getTree: jest.fn().mockResolvedValue({ baseComponent: {}, components: [], paging: {} }), | |||
searchProjects: jest.fn().mockResolvedValue({ | |||
components: [], | |||
facets: [], | |||
paging: {}, | |||
}), | |||
})); | |||
beforeEach(() => jest.clearAllMocks()); | |||
it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot(); | |||
}); | |||
it('should callback to load search results', () => { | |||
const loadSearchResultCount = jest.fn(); | |||
const wrapper = shallowRender({ loadSearchResultCount }); | |||
wrapper.instance().loadSearchResultCount([ | |||
{ key: '1', name: 'first' }, | |||
{ key: '2', name: 'seecond' }, | |||
]); | |||
expect(loadSearchResultCount).toHaveBeenCalledWith('projects', { projects: ['1', '2'] }); | |||
}); | |||
it('should handle search for projects globally', async () => { | |||
const wrapper = shallowRender(); | |||
const query = 'my project'; | |||
await wrapper.instance().handleSearch(query); | |||
expect(searchProjects).toHaveBeenCalled(); | |||
expect(getTree).not.toHaveBeenCalled(); | |||
}); | |||
it('should handle search for projects in portfolio', async () => { | |||
const wrapper = shallowRender({ | |||
component: mockComponent({ qualifier: ComponentQualifier.Portfolio }), | |||
}); | |||
const query = 'my project'; | |||
await wrapper.instance().handleSearch(query); | |||
expect(searchProjects).not.toHaveBeenCalled(); | |||
expect(getTree).toHaveBeenCalled(); | |||
}); | |||
describe("ListStyleFacet's renderers", () => { | |||
const components: ReferencedComponent[] = [ | |||
{ key: 'projectKey', name: 'First Project Name', uuid: '141324' }, | |||
{ key: 'projectKey2', name: 'Second Project Name', uuid: '643878' }, | |||
]; | |||
const referencedComponents = keyBy(components, (c) => c.key); | |||
const wrapper = shallowRender({ referencedComponents }); | |||
const instance = wrapper.instance(); | |||
it('should include getProjectName', () => { | |||
expect(instance.getProjectName(components[0].key)).toBe(components[0].name); | |||
expect(instance.getProjectName('nonexistent')).toBe('nonexistent'); | |||
}); | |||
it('should include renderFacetItem', () => { | |||
expect(instance.renderFacetItem(components[0].key)).toMatchSnapshot(); | |||
}); | |||
it('should include renderSearchResult', () => { | |||
expect(instance.renderSearchResult(components[0], 'First')).toMatchSnapshot(); | |||
}); | |||
}); | |||
function shallowRender(props: Partial<ProjectFacet['props']> = {}) { | |||
return shallow<ProjectFacet>( | |||
<ProjectFacet | |||
component={undefined} | |||
fetching={false} | |||
loadSearchResultCount={jest.fn()} | |||
onChange={jest.fn()} | |||
onToggle={jest.fn()} | |||
open={false} | |||
projects={[]} | |||
query={{} as Query} | |||
referencedComponents={{}} | |||
stats={undefined} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,81 +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 { searchRules } from '../../../../api/rules'; | |||
import { mockReferencedRule } from '../../../../helpers/mocks/issues'; | |||
import { mockRule } from '../../../../helpers/testMocks'; | |||
import { Query } from '../../utils'; | |||
import RuleFacet from '../RuleFacet'; | |||
jest.mock('../../../../api/rules', () => ({ | |||
searchRules: jest.fn().mockResolvedValue({}), | |||
})); | |||
it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot(); | |||
}); | |||
it('should handle search', async () => { | |||
const wrapper = shallowRender(); | |||
const query = 'query'; | |||
await wrapper.instance().handleSearch(query); | |||
expect(searchRules).toHaveBeenCalledWith( | |||
expect.objectContaining({ languages: 'js,java', q: query }) | |||
); | |||
}); | |||
describe('ListStyleFacet Renderers', () => { | |||
const referencedRules = { r1: mockReferencedRule() }; | |||
const instance = shallowRender({ referencedRules }).instance(); | |||
it('should include renderFacetItem', () => { | |||
const rule = referencedRules.r1; | |||
expect(instance.getRuleName('r1')).toBe(`(${rule.langName}) ${rule.name}`); | |||
expect(instance.getRuleName('nonexistent')).toBe('nonexistent'); | |||
}); | |||
it('should include renderSearchResult', () => { | |||
const rule = mockRule(); | |||
expect(instance.renderSearchResult(rule)).toBe(`(${rule.langName}) ${rule.name}`); | |||
expect(instance.renderSearchResult(mockRule({ langName: '' }))).toBe(rule.name); | |||
}); | |||
}); | |||
function shallowRender(props: Partial<RuleFacet['props']> = {}) { | |||
return shallow<RuleFacet>( | |||
<RuleFacet | |||
fetching={true} | |||
languages={['js', 'java']} | |||
loadSearchResultCount={jest.fn()} | |||
onChange={jest.fn()} | |||
onToggle={jest.fn()} | |||
open={false} | |||
query={{} as Query} | |||
referencedRules={{}} | |||
rules={['r1']} | |||
stats={{}} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,93 +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, ShallowWrapper } from 'enzyme'; | |||
import * as React from 'react'; | |||
import FacetHeader from '../../../../components/facet/FacetHeader'; | |||
import FacetItem from '../../../../components/facet/FacetItem'; | |||
import { IssueScope } from '../../../../types/issues'; | |||
import ScopeFacet, { ScopeFacetProps } from '../ScopeFacet'; | |||
it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot('default'); | |||
expect(shallowRender({ open: true })).toMatchSnapshot('open'); | |||
expect(shallowRender({ open: true, scopes: [IssueScope.Main] })).toMatchSnapshot('active facet'); | |||
expect(shallowRender({ open: true, stats: { [IssueScope.Main]: 0 } })).toMatchSnapshot( | |||
'disabled facet' | |||
); | |||
}); | |||
it('should correctly handle facet header clicks', () => { | |||
const onChange = jest.fn(); | |||
const onToggle = jest.fn(); | |||
const wrapper = shallowRender({ onChange, onToggle }); | |||
wrapper.find(FacetHeader).props().onClear!(); | |||
expect(onChange).toHaveBeenCalledWith({ scopes: [] }); | |||
wrapper.find(FacetHeader).props().onClick!(); | |||
expect(onToggle).toHaveBeenCalledWith('scopes'); | |||
}); | |||
it('should correctly handle facet item clicks', () => { | |||
const wrapper = shallowRender({ open: true, scopes: [IssueScope.Main] }); | |||
const onChange = jest.fn(({ scopes }) => wrapper.setProps({ scopes })); | |||
wrapper.setProps({ onChange }); | |||
clickFacetItem(wrapper, IssueScope.Test); | |||
expect(onChange).toHaveBeenLastCalledWith({ scopes: [IssueScope.Test] }); | |||
clickFacetItem(wrapper, IssueScope.Test); | |||
expect(onChange).toHaveBeenLastCalledWith({ scopes: [] }); | |||
clickFacetItem(wrapper, IssueScope.Test, true); | |||
clickFacetItem(wrapper, IssueScope.Main, true); | |||
expect(onChange).toHaveBeenLastCalledWith({ | |||
scopes: expect.arrayContaining([IssueScope.Main, IssueScope.Test]), | |||
}); | |||
clickFacetItem(wrapper, IssueScope.Test, true); | |||
expect(onChange).toHaveBeenLastCalledWith({ scopes: [IssueScope.Main] }); | |||
}); | |||
function clickFacetItem( | |||
wrapper: ShallowWrapper<ScopeFacetProps>, | |||
scope: IssueScope, | |||
multiple = false | |||
) { | |||
return wrapper | |||
.find(FacetItem) | |||
.filterWhere((f) => f.key() === scope) | |||
.props() | |||
.onClick(scope, multiple); | |||
} | |||
function shallowRender(props: Partial<ScopeFacetProps> = {}) { | |||
return shallow<ScopeFacetProps>( | |||
<ScopeFacet | |||
fetching={true} | |||
onChange={jest.fn()} | |||
onToggle={jest.fn()} | |||
open={false} | |||
scopes={[]} | |||
stats={{}} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -0,0 +1,129 @@ | |||
/* | |||
* 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 * as React from 'react'; | |||
import { mockComponent } from '../../../../helpers/mocks/component'; | |||
import { mockQuery } from '../../../../helpers/mocks/issues'; | |||
import { mockAppState } from '../../../../helpers/testMocks'; | |||
import { renderComponent } from '../../../../helpers/testReactTestingUtils'; | |||
import { ComponentQualifier } from '../../../../types/component'; | |||
import { GlobalSettingKeys } from '../../../../types/settings'; | |||
import { Sidebar } from '../Sidebar'; | |||
it('should render correct facets for Application', () => { | |||
renderSidebar({ component: mockComponent({ qualifier: ComponentQualifier.Application }) }); | |||
expect(screen.getAllByRole('button').map((button) => button.textContent)).toStrictEqual([ | |||
'issues.facet.types', | |||
'issues.facet.severities', | |||
'issues.facet.scopes', | |||
'issues.facet.resolutions', | |||
'issues.facet.statuses', | |||
'issues.facet.standards', | |||
'issues.facet.createdAt', | |||
'issues.facet.languages', | |||
'issues.facet.rules', | |||
'issues.facet.tags', | |||
'issues.facet.projects', | |||
'issues.facet.assignees', | |||
'clear', | |||
'issues.facet.authors', | |||
]); | |||
}); | |||
it('should render correct facets for Portfolio', () => { | |||
renderSidebar({ component: mockComponent({ qualifier: ComponentQualifier.Portfolio }) }); | |||
expect(screen.getAllByRole('button').map((button) => button.textContent)).toStrictEqual([ | |||
'issues.facet.types', | |||
'issues.facet.severities', | |||
'issues.facet.scopes', | |||
'issues.facet.resolutions', | |||
'issues.facet.statuses', | |||
'issues.facet.standards', | |||
'issues.facet.createdAt', | |||
'issues.facet.languages', | |||
'issues.facet.rules', | |||
'issues.facet.tags', | |||
'issues.facet.projects', | |||
'issues.facet.assignees', | |||
'clear', | |||
'issues.facet.authors', | |||
]); | |||
}); | |||
it('should render correct facets for SubPortfolio', () => { | |||
renderSidebar({ component: mockComponent({ qualifier: ComponentQualifier.SubPortfolio }) }); | |||
expect(screen.getAllByRole('button').map((button) => button.textContent)).toStrictEqual([ | |||
'issues.facet.types', | |||
'issues.facet.severities', | |||
'issues.facet.scopes', | |||
'issues.facet.resolutions', | |||
'issues.facet.statuses', | |||
'issues.facet.standards', | |||
'issues.facet.createdAt', | |||
'issues.facet.languages', | |||
'issues.facet.rules', | |||
'issues.facet.tags', | |||
'issues.facet.projects', | |||
'issues.facet.assignees', | |||
'clear', | |||
'issues.facet.authors', | |||
]); | |||
}); | |||
it.each([ | |||
['week', '1w'], | |||
['month', '1m'], | |||
['year', '1y'], | |||
])('should render correctly for createdInLast %s', (name, createdInLast) => { | |||
renderSidebar({ component: mockComponent(), query: mockQuery({ createdInLast }) }); | |||
const text = { | |||
week: 'issues.facet.createdAt.last_week', | |||
month: 'issues.facet.createdAt.last_month', | |||
year: 'issues.facet.createdAt.last_year', | |||
}[name] as string; | |||
expect(screen.getByText(text)).toBeInTheDocument(); | |||
}); | |||
function renderSidebar(props: Partial<Sidebar['props']> = {}) { | |||
return renderComponent( | |||
<Sidebar | |||
appState={mockAppState({ | |||
settings: { [GlobalSettingKeys.DeveloperAggregatedInfoDisabled]: 'false' }, | |||
})} | |||
component={mockComponent()} | |||
createdAfterIncludesTime={false} | |||
facets={{}} | |||
loadSearchResultCount={jest.fn()} | |||
loadingFacets={{}} | |||
myIssues={false} | |||
onFacetToggle={jest.fn()} | |||
onFilterChange={jest.fn()} | |||
openFacets={{}} | |||
query={mockQuery()} | |||
referencedComponentsById={{}} | |||
referencedComponentsByKey={{}} | |||
referencedLanguages={{}} | |||
referencedRules={{}} | |||
referencedUsers={{}} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,97 +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, ShallowWrapper } from 'enzyme'; | |||
import { flatten } from 'lodash'; | |||
import * as React from 'react'; | |||
import { mockComponent } from '../../../../helpers/mocks/component'; | |||
import { mockAppState } from '../../../../helpers/testMocks'; | |||
import { ComponentQualifier } from '../../../../types/component'; | |||
import { GlobalSettingKeys } from '../../../../types/settings'; | |||
import { Query } from '../../utils'; | |||
import { Sidebar } from '../Sidebar'; | |||
it('should render facets for global page', () => { | |||
expect(renderSidebar()).toMatchSnapshot(); | |||
}); | |||
it('should render facets for project', () => { | |||
expect(renderSidebar({ component: mockComponent() })).toMatchSnapshot(); | |||
}); | |||
it.each([ | |||
[ComponentQualifier.Application], | |||
[ComponentQualifier.Portfolio], | |||
[ComponentQualifier.SubPortfolio], | |||
])('should render facets for %p', (qualifier) => { | |||
expect(renderSidebar({ component: mockComponent({ qualifier }) })).toMatchSnapshot(); | |||
}); | |||
it('should render facets when my issues are selected', () => { | |||
expect(renderSidebar({ myIssues: true })).toMatchSnapshot(); | |||
}); | |||
it('should not render developer nominative facets when asked not to', () => { | |||
expect( | |||
renderSidebar({ | |||
appState: mockAppState({ | |||
settings: { [GlobalSettingKeys.DeveloperAggregatedInfoDisabled]: 'true' }, | |||
}), | |||
}) | |||
).toMatchSnapshot(); | |||
}); | |||
const renderSidebar = (props?: Partial<Sidebar['props']>) => { | |||
return flatten( | |||
mapChildren( | |||
shallow<Sidebar>( | |||
<Sidebar | |||
appState={mockAppState({ | |||
settings: { [GlobalSettingKeys.DeveloperAggregatedInfoDisabled]: 'false' }, | |||
})} | |||
component={undefined} | |||
createdAfterIncludesTime={false} | |||
facets={{}} | |||
loadSearchResultCount={jest.fn()} | |||
loadingFacets={{}} | |||
myIssues={false} | |||
onFacetToggle={jest.fn()} | |||
onFilterChange={jest.fn()} | |||
openFacets={{}} | |||
query={{ types: [''] } as Query} | |||
referencedComponentsById={{}} | |||
referencedComponentsByKey={{}} | |||
referencedLanguages={{}} | |||
referencedRules={{}} | |||
referencedUsers={{}} | |||
{...props} | |||
/> | |||
) | |||
) | |||
); | |||
function mapChildren(wrapper: ShallowWrapper) { | |||
return wrapper.children().map((node) => { | |||
if (typeof node.type() === 'symbol') { | |||
return node.children().map((n) => n.name()); | |||
} | |||
return node.name(); | |||
}); | |||
} | |||
}; |
@@ -1,267 +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 ListStyleFacetFooter from '../../../../components/facet/ListStyleFacetFooter'; | |||
import { getStandards } from '../../../../helpers/security-standard'; | |||
import { click } from '../../../../helpers/testUtils'; | |||
import { Query } from '../../utils'; | |||
import StandardFacet from '../StandardFacet'; | |||
jest.mock('../../../../helpers/security-standard', () => ({ | |||
...jest.requireActual('../../../../helpers/security-standard'), | |||
getStandards: jest.fn().mockResolvedValue({ | |||
owaspTop10: { | |||
a1: { | |||
title: 'Injection', | |||
}, | |||
a2: { | |||
title: 'Broken Authentication', | |||
}, | |||
}, | |||
'owaspTop10-2021': { | |||
a1: { | |||
title: 'Injection', | |||
}, | |||
a2: { | |||
title: 'Broken Authentication', | |||
}, | |||
}, | |||
cwe: { | |||
unknown: { | |||
title: 'No CWE associated', | |||
}, | |||
'1004': { | |||
title: "Sensitive Cookie Without 'HttpOnly' Flag", | |||
}, | |||
}, | |||
sonarsourceSecurity: { | |||
'sql-injection': { | |||
title: 'SQL Injection', | |||
}, | |||
'command-injection': { | |||
title: 'Command Injection', | |||
}, | |||
}, | |||
}), | |||
})); | |||
it('should render closed', () => { | |||
expect(shallowRender()).toMatchSnapshot(); | |||
expect(getStandards).not.toHaveBeenCalled(); | |||
}); | |||
it('should toggle standards facet', () => { | |||
const onToggle = jest.fn(); | |||
const wrapper = shallowRender({ onToggle }); | |||
click(wrapper.children('FacetHeader')); | |||
expect(onToggle).toHaveBeenCalledWith('standards'); | |||
}); | |||
it('should clear standards facet', () => { | |||
const onChange = jest.fn(); | |||
const wrapper = shallowRender({ onChange }); | |||
wrapper.children('FacetHeader').prop<Function>('onClear')(); | |||
expect(onChange).toHaveBeenCalledWith({ | |||
cwe: [], | |||
owaspTop10: [], | |||
'owaspTop10-2021': [], | |||
sonarsourceSecurity: [], | |||
standards: [], | |||
}); | |||
}); | |||
it('should render sub-facets', () => { | |||
expect( | |||
shallowRender({ | |||
cwe: ['42'], | |||
cweOpen: true, | |||
cweStats: { 42: 5, 173: 3 }, | |||
open: true, | |||
owaspTop10: ['a3'], | |||
owaspTop10Open: true, | |||
owaspTop10Stats: { a1: 15, a3: 5 }, | |||
sonarsourceSecurity: ['sql-injection'], | |||
sonarsourceSecurityOpen: true, | |||
sonarsourceSecurityStats: { 'sql-injection': 12 }, | |||
}) | |||
).toMatchSnapshot(); | |||
expect(getStandards).toHaveBeenCalled(); | |||
}); | |||
it('should show sonarsource facet more button', () => { | |||
const wrapper = shallowRender({ | |||
open: true, | |||
sonarsourceSecurity: ['traceability', 'permission', 'others'], | |||
sonarsourceSecurityOpen: true, | |||
sonarsourceSecurityStats: { | |||
'buffer-overflow': 3, | |||
'sql-injection': 3, | |||
rce: 3, | |||
'object-injection': 3, | |||
'command-injection': 3, | |||
'path-traversal-injection': 3, | |||
'ldap-injection': 3, | |||
'xpath-injection': 3, | |||
'expression-lang-injection': 3, | |||
'log-injection': 3, | |||
xxe: 3, | |||
xss: 3, | |||
dos: 3, | |||
ssrf: 3, | |||
csrf: 3, | |||
'http-response-splitting': 3, | |||
'open-redirect': 3, | |||
'weak-cryptography': 3, | |||
auth: 3, | |||
'insecure-conf': 3, | |||
'file-manipulation': 3, | |||
'encrypt-data': 3, | |||
traceability: 3, | |||
permission: 3, | |||
others: 3, | |||
}, | |||
}); | |||
expect(wrapper.find(ListStyleFacetFooter).exists()).toBe(true); | |||
wrapper.setState({ showFullSonarSourceList: true }); | |||
expect(wrapper.find(ListStyleFacetFooter).exists()).toBe(false); | |||
}); | |||
it('should render empty sub-facet', () => { | |||
expect( | |||
shallowRender({ | |||
open: true, | |||
'owaspTop10-2021': [], | |||
'owaspTop10-2021Open': true, | |||
'owaspTop10-2021Stats': {}, | |||
}).find('FacetBox[property="owaspTop10-2021"]') | |||
).toMatchSnapshot(); | |||
}); | |||
it('should select items', () => { | |||
const onChange = jest.fn(); | |||
const wrapper = shallowRender({ | |||
cwe: ['42'], | |||
cweOpen: true, | |||
cweStats: { 42: 5, 173: 3 }, | |||
onChange, | |||
open: true, | |||
owaspTop10: ['a3'], | |||
owaspTop10Open: true, | |||
owaspTop10Stats: { a1: 15, a3: 5 }, | |||
sonarsourceSecurity: ['command-injection'], | |||
sonarsourceSecurityOpen: true, | |||
sonarsourceSecurityStats: { 'sql-injection': 10 }, | |||
}); | |||
selectAndCheck('owaspTop10', 'a1'); | |||
selectAndCheck('owaspTop10', 'a1', true, ['a1', 'a3']); | |||
selectAndCheck('sonarsourceSecurity', 'sql-injection'); | |||
function selectAndCheck(facet: string, value: string, multiple = false, expectedValue = [value]) { | |||
wrapper | |||
.find(`FacetBox[property="${facet}"]`) | |||
.find(`FacetItem[value="${value}"]`) | |||
.prop<Function>('onClick')(value, multiple); | |||
expect(onChange).toHaveBeenLastCalledWith({ [facet]: expectedValue }); | |||
} | |||
}); | |||
it('should toggle sub-facets', () => { | |||
const onToggle = jest.fn(); | |||
const wrapper = shallowRender({ onToggle, open: true }); | |||
click(wrapper.find('FacetBox[property="owaspTop10"]').children('FacetHeader')); | |||
expect(onToggle).toHaveBeenLastCalledWith('owaspTop10'); | |||
click(wrapper.find('FacetBox[property="sonarsourceSecurity"]').children('FacetHeader')); | |||
expect(onToggle).toHaveBeenLastCalledWith('sonarsourceSecurity'); | |||
}); | |||
it('should display correct selection', () => { | |||
const wrapper = shallowRender({ | |||
open: true, | |||
owaspTop10: ['a1', 'a3'], | |||
'owaspTop10-2021': ['a1', 'a2'], | |||
cwe: ['42', '1111', 'unknown'], | |||
sonarsourceSecurity: ['sql-injection', 'others'], | |||
}); | |||
checkValues('standards', [ | |||
'SONAR SQL Injection', | |||
'Others', | |||
'OWASP A1 - a1 title', | |||
'OWASP A3', | |||
'OWASP A1 - a1 title', | |||
'OWASP A2', | |||
'CWE-42 - cwe-42 title', | |||
'CWE-1111', | |||
'Unknown CWE', | |||
]); | |||
checkValues('owaspTop10', ['A1 - a1 title', 'A3']); | |||
checkValues('owaspTop10-2021', ['A1 - a1 title', 'A2']); | |||
checkValues('sonarsourceSecurity', ['SQL Injection', 'Others']); | |||
function checkValues(property: string, values: string[]) { | |||
expect( | |||
wrapper.find(`FacetBox[property="${property}"]`).children('FacetHeader').prop('values') | |||
).toEqual(values); | |||
} | |||
}); | |||
function shallowRender(props: Partial<StandardFacet['props']> = {}) { | |||
const wrapper = shallow( | |||
<StandardFacet | |||
cwe={[]} | |||
cweOpen={false} | |||
cweStats={{}} | |||
fetchingCwe={false} | |||
fetchingOwaspTop10={false} | |||
fetchingOwaspTop10-2021={false} | |||
fetchingSonarSourceSecurity={false} | |||
loadSearchResultCount={jest.fn()} | |||
onChange={jest.fn()} | |||
onToggle={jest.fn()} | |||
open={false} | |||
owaspTop10={[]} | |||
owaspTop10Open={false} | |||
owaspTop10Stats={{}} | |||
owaspTop10-2021={[]} | |||
owaspTop10-2021Open={false} | |||
owaspTop10-2021Stats={{}} | |||
query={{} as Query} | |||
sonarsourceSecurity={[]} | |||
sonarsourceSecurityOpen={false} | |||
sonarsourceSecurityStats={{}} | |||
{...props} | |||
/> | |||
); | |||
wrapper.setState({ | |||
standards: { | |||
owaspTop10: { a1: { title: 'a1 title' } }, | |||
'owaspTop10-2021': { a1: { title: 'a1 title' } }, | |||
cwe: { 42: { title: 'cwe-42 title' }, unknown: { title: 'Unknown CWE' } }, | |||
sonarsourceSecurity: { | |||
'sql-injection': { title: 'SQL Injection' }, | |||
others: { title: 'Others' }, | |||
}, | |||
}, | |||
}); | |||
return wrapper; | |||
} |
@@ -1,78 +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 StatusFacet from '../StatusFacet'; | |||
it('should render correctly', () => { | |||
expect(shallowRender()).toMatchSnapshot(); | |||
}); | |||
it('should toggle status facet', () => { | |||
const onToggle = jest.fn(); | |||
const wrapper = shallowRender({ onToggle }); | |||
click(wrapper.children('FacetHeader')); | |||
expect(onToggle).toHaveBeenCalledWith('statuses'); | |||
}); | |||
it('should clear status facet', () => { | |||
const onChange = jest.fn(); | |||
const wrapper = shallowRender({ onChange, statuses: ['CONFIRMED'] }); | |||
wrapper.children('FacetHeader').prop<Function>('onClear')(); | |||
expect(onChange).toHaveBeenCalledWith({ statuses: [] }); | |||
}); | |||
it('should select a status', () => { | |||
const onChange = jest.fn(); | |||
const wrapper = shallowRender({ onChange }); | |||
clickAndCheck('OPEN'); | |||
clickAndCheck('CONFIRMED', true, ['CONFIRMED', 'OPEN']); | |||
clickAndCheck('CLOSED'); | |||
function clickAndCheck(status: string, multiple = false, expected = [status]) { | |||
wrapper.find(`FacetItemsList`).find(`FacetItem[value="${status}"]`).prop<Function>('onClick')( | |||
status, | |||
multiple | |||
); | |||
expect(onChange).toHaveBeenLastCalledWith({ statuses: expected }); | |||
wrapper.setProps({ statuses: expected }); | |||
} | |||
}); | |||
function shallowRender(props: Partial<StatusFacet['props']> = {}) { | |||
return shallow( | |||
<StatusFacet | |||
fetching={false} | |||
onChange={jest.fn()} | |||
onToggle={jest.fn()} | |||
open={true} | |||
stats={{ | |||
OPEN: 104, | |||
CONFIRMED: 8, | |||
REOPENED: 0, | |||
RESOLVED: 0, | |||
CLOSED: 8, | |||
}} | |||
statuses={[]} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,70 +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 TypeFacet from '../TypeFacet'; | |||
it('should render open by default', () => { | |||
expect(shallowRender({ types: ['VULNERABILITY', 'CODE_SMELL'] })).toMatchSnapshot(); | |||
}); | |||
it('should toggle type facet', () => { | |||
const onToggle = jest.fn(); | |||
const wrapper = shallowRender({ onToggle }); | |||
click(wrapper.children('FacetHeader')); | |||
expect(onToggle).toHaveBeenCalledWith('types'); | |||
}); | |||
it('should clear types facet', () => { | |||
const onChange = jest.fn(); | |||
const wrapper = shallowRender({ onChange, types: ['BUGS'] }); | |||
wrapper.children('FacetHeader').prop<Function>('onClear')(); | |||
expect(onChange).toHaveBeenCalledWith({ types: [] }); | |||
}); | |||
it('should select a type', () => { | |||
const onChange = jest.fn(); | |||
const wrapper = shallowRender({ onChange }); | |||
clickAndCheck('CODE_SMELL'); | |||
clickAndCheck('VULNERABILITY', true, ['CODE_SMELL', 'VULNERABILITY']); | |||
function clickAndCheck(type: string, multiple = false, expected = [type]) { | |||
wrapper.find(`FacetItemsList`).find(`FacetItem[value="${type}"]`).prop<Function>('onClick')( | |||
type, | |||
multiple | |||
); | |||
expect(onChange).toHaveBeenLastCalledWith({ types: expected }); | |||
wrapper.setProps({ types: expected }); | |||
} | |||
}); | |||
function shallowRender(props: Partial<TypeFacet['props']> = {}) { | |||
return shallow( | |||
<TypeFacet | |||
fetching={false} | |||
onChange={jest.fn()} | |||
onToggle={jest.fn()} | |||
stats={{ BUG: 0, VULNERABILITY: 2, CODE_SMELL: 5, SECURITY_HOTSPOT: 1 }} | |||
types={[]} | |||
{...props} | |||
/> | |||
); | |||
} |
@@ -1,96 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render 1`] = ` | |||
<ListStyleFacet | |||
facetHeader="issues.facet.assignees" | |||
fetching={false} | |||
getFacetItemText={[Function]} | |||
getSearchResultKey={[Function]} | |||
getSearchResultText={[Function]} | |||
getSortedItems={[Function]} | |||
loadSearchResultCount={[Function]} | |||
maxInitialItems={15} | |||
maxItems={100} | |||
minSearchLength={2} | |||
onChange={[MockFunction]} | |||
onClear={[Function]} | |||
onItemClick={[Function]} | |||
onSearch={[Function]} | |||
onToggle={[MockFunction]} | |||
open={true} | |||
property="assignees" | |||
query={{}} | |||
renderFacetItem={[Function]} | |||
renderSearchResult={[Function]} | |||
searchPlaceholder="search.search_for_users" | |||
stats={ | |||
{ | |||
"": 5, | |||
"bar": 7, | |||
"baz": 6, | |||
"foo": 13, | |||
} | |||
} | |||
values={ | |||
[ | |||
"foo", | |||
] | |||
} | |||
/> | |||
`; | |||
exports[`test behavior should correctly render facet item 1`] = ` | |||
<React.Fragment> | |||
<withAppStateContext(Avatar) | |||
className="little-spacer-right" | |||
name="Name Baz" | |||
size={16} | |||
/> | |||
Name Baz | |||
</React.Fragment> | |||
`; | |||
exports[`test behavior should correctly render facet item 2`] = ` | |||
<React.Fragment> | |||
<withAppStateContext(Avatar) | |||
className="little-spacer-right" | |||
name="foo" | |||
size={16} | |||
/> | |||
user.x_deleted.foo | |||
</React.Fragment> | |||
`; | |||
exports[`test behavior should correctly render search result correctly 1`] = ` | |||
<React.Fragment> | |||
<withAppStateContext(Avatar) | |||
className="little-spacer-right" | |||
name="Name Bar" | |||
size={16} | |||
/> | |||
<React.Fragment> | |||
Name | |||
<mark> | |||
Ba | |||
</mark> | |||
r | |||
</React.Fragment> | |||
</React.Fragment> | |||
`; | |||
exports[`test behavior should correctly render search result correctly 2`] = ` | |||
<React.Fragment> | |||
<withAppStateContext(Avatar) | |||
className="little-spacer-right" | |||
name="foo" | |||
size={16} | |||
/> | |||
<React.Fragment> | |||
user.x_deleted. | |||
<mark> | |||
fo | |||
</mark> | |||
o | |||
</React.Fragment> | |||
</React.Fragment> | |||
`; |
@@ -1,26 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<ListStyleFacet | |||
facetHeader="issues.facet.authors" | |||
fetching={false} | |||
getFacetItemText={[Function]} | |||
getSearchResultKey={[Function]} | |||
getSearchResultText={[Function]} | |||
loadSearchResultCount={[Function]} | |||
maxInitialItems={15} | |||
maxItems={100} | |||
minSearchLength={2} | |||
onChange={[MockFunction]} | |||
onSearch={[Function]} | |||
onToggle={[MockFunction]} | |||
open={true} | |||
property="author" | |||
query={{}} | |||
renderFacetItem={[Function]} | |||
renderSearchResult={[Function]} | |||
searchPlaceholder="search.search_for_authors" | |||
stats={{}} | |||
values={[]} | |||
/> | |||
`; |
@@ -1,548 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly for createdInLast month 1`] = ` | |||
<FacetBox | |||
property="createdAt" | |||
> | |||
<FacetHeader | |||
fetching={false} | |||
name="issues.facet.createdAt" | |||
onClear={[Function]} | |||
onClick={[Function]} | |||
open={true} | |||
values={ | |||
[ | |||
"issues.facet.createdAt.last_month", | |||
] | |||
} | |||
/> | |||
<div> | |||
<div | |||
className="search-navigator-date-facet-selection" | |||
> | |||
<DateRangeInput | |||
alignEndDateCalandarRight={true} | |||
onChange={[Function]} | |||
value={ | |||
{ | |||
"from": undefined, | |||
"to": undefined, | |||
} | |||
} | |||
/> | |||
</div> | |||
<div | |||
className="spacer-top issues-predefined-periods" | |||
> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.all" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.all" | |||
value="" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.last_week" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.last_week" | |||
value="1w" | |||
/> | |||
<FacetItem | |||
active={true} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.last_month" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.last_month" | |||
value="1m" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.last_year" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.last_year" | |||
value="1y" | |||
/> | |||
</div> | |||
</div> | |||
</FacetBox> | |||
`; | |||
exports[`should render correctly for createdInLast week 1`] = ` | |||
<FacetBox | |||
property="createdAt" | |||
> | |||
<FacetHeader | |||
fetching={false} | |||
name="issues.facet.createdAt" | |||
onClear={[Function]} | |||
onClick={[Function]} | |||
open={true} | |||
values={ | |||
[ | |||
"issues.facet.createdAt.last_week", | |||
] | |||
} | |||
/> | |||
<div> | |||
<div | |||
className="search-navigator-date-facet-selection" | |||
> | |||
<DateRangeInput | |||
alignEndDateCalandarRight={true} | |||
onChange={[Function]} | |||
value={ | |||
{ | |||
"from": undefined, | |||
"to": undefined, | |||
} | |||
} | |||
/> | |||
</div> | |||
<div | |||
className="spacer-top issues-predefined-periods" | |||
> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.all" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.all" | |||
value="" | |||
/> | |||
<FacetItem | |||
active={true} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.last_week" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.last_week" | |||
value="1w" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.last_month" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.last_month" | |||
value="1m" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.last_year" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.last_year" | |||
value="1y" | |||
/> | |||
</div> | |||
</div> | |||
</FacetBox> | |||
`; | |||
exports[`should render correctly for createdInLast year 1`] = ` | |||
<FacetBox | |||
property="createdAt" | |||
> | |||
<FacetHeader | |||
fetching={false} | |||
name="issues.facet.createdAt" | |||
onClear={[Function]} | |||
onClick={[Function]} | |||
open={true} | |||
values={ | |||
[ | |||
"issues.facet.createdAt.last_year", | |||
] | |||
} | |||
/> | |||
<div> | |||
<div | |||
className="search-navigator-date-facet-selection" | |||
> | |||
<DateRangeInput | |||
alignEndDateCalandarRight={true} | |||
onChange={[Function]} | |||
value={ | |||
{ | |||
"from": undefined, | |||
"to": undefined, | |||
} | |||
} | |||
/> | |||
</div> | |||
<div | |||
className="spacer-top issues-predefined-periods" | |||
> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.all" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.all" | |||
value="" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.last_week" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.last_week" | |||
value="1w" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.last_month" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.last_month" | |||
value="1m" | |||
/> | |||
<FacetItem | |||
active={true} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.last_year" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.last_year" | |||
value="1y" | |||
/> | |||
</div> | |||
</div> | |||
</FacetBox> | |||
`; | |||
exports[`should render correctly: clear 1`] = ` | |||
<FacetBox | |||
property="createdAt" | |||
> | |||
<FacetHeader | |||
fetching={false} | |||
name="issues.facet.createdAt" | |||
onClear={[Function]} | |||
onClick={[Function]} | |||
open={true} | |||
values={[]} | |||
/> | |||
<div> | |||
<div | |||
className="search-navigator-date-facet-selection" | |||
> | |||
<DateRangeInput | |||
alignEndDateCalandarRight={true} | |||
onChange={[Function]} | |||
value={ | |||
{ | |||
"from": undefined, | |||
"to": undefined, | |||
} | |||
} | |||
/> | |||
</div> | |||
<div | |||
className="spacer-top issues-predefined-periods" | |||
> | |||
<FacetItem | |||
active={true} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.all" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.all" | |||
value="" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.last_week" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.last_week" | |||
value="1w" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.last_month" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.last_month" | |||
value="1m" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.last_year" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.last_year" | |||
value="1y" | |||
/> | |||
</div> | |||
</div> | |||
</FacetBox> | |||
`; | |||
exports[`should render correctly: closed 1`] = ` | |||
<FacetBox | |||
property="createdAt" | |||
> | |||
<FacetHeader | |||
fetching={false} | |||
name="issues.facet.createdAt" | |||
onClear={[Function]} | |||
onClick={[Function]} | |||
open={false} | |||
values={[]} | |||
/> | |||
</FacetBox> | |||
`; | |||
exports[`should render correctly: created after 1`] = ` | |||
<FacetBox | |||
property="createdAt" | |||
> | |||
<FacetHeader | |||
fetching={false} | |||
name="issues.facet.createdAt" | |||
onClear={[Function]} | |||
onClick={[Function]} | |||
open={true} | |||
values={ | |||
[ | |||
"formatted.Invalid Date", | |||
] | |||
} | |||
/> | |||
<div | |||
className="search-navigator-facet-container" | |||
> | |||
<strong> | |||
after | |||
</strong> | |||
<DateTimeFormatter | |||
date={Date { NaN }} | |||
/> | |||
</div> | |||
</FacetBox> | |||
`; | |||
exports[`should render correctly: created after timestamp 1`] = ` | |||
<FacetBox | |||
property="createdAt" | |||
> | |||
<FacetHeader | |||
fetching={false} | |||
name="issues.facet.createdAt" | |||
onClear={[Function]} | |||
onClick={[Function]} | |||
open={true} | |||
values={ | |||
[ | |||
"formatted.Invalid Date", | |||
] | |||
} | |||
/> | |||
<div | |||
className="search-navigator-facet-container" | |||
> | |||
<strong> | |||
after | |||
</strong> | |||
<DateTimeFormatter | |||
date={Date { NaN }} | |||
/> | |||
</div> | |||
</FacetBox> | |||
`; | |||
exports[`should render correctly: created at 1`] = ` | |||
<FacetBox | |||
property="createdAt" | |||
> | |||
<FacetHeader | |||
fetching={false} | |||
name="issues.facet.createdAt" | |||
onClear={[Function]} | |||
onClick={[Function]} | |||
open={true} | |||
values={ | |||
[ | |||
"formatted.2019.05.21T13:33:00Z", | |||
] | |||
} | |||
/> | |||
<div | |||
className="search-navigator-facet-container" | |||
> | |||
<DateTimeFormatter | |||
date="2019.05.21T13:33:00Z" | |||
/> | |||
<br /> | |||
<span | |||
className="note" | |||
> | |||
<DateFromNow | |||
date="2019.05.21T13:33:00Z" | |||
/> | |||
</span> | |||
</div> | |||
</FacetBox> | |||
`; | |||
exports[`should render correctly: portfolio 1`] = ` | |||
<FacetBox | |||
property="createdAt" | |||
> | |||
<FacetHeader | |||
fetching={false} | |||
name="issues.facet.createdAt" | |||
onClear={[Function]} | |||
onClick={[Function]} | |||
open={true} | |||
values={[]} | |||
/> | |||
<div> | |||
<div | |||
className="search-navigator-date-facet-selection" | |||
> | |||
<DateRangeInput | |||
alignEndDateCalandarRight={true} | |||
onChange={[Function]} | |||
value={ | |||
{ | |||
"from": undefined, | |||
"to": undefined, | |||
} | |||
} | |||
/> | |||
</div> | |||
<div | |||
className="spacer-top issues-predefined-periods" | |||
> | |||
<FacetItem | |||
active={true} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.all" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.all" | |||
value="" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.last_week" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.last_week" | |||
value="1w" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.last_month" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.last_month" | |||
value="1m" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.last_year" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.last_year" | |||
value="1y" | |||
/> | |||
</div> | |||
</div> | |||
</FacetBox> | |||
`; | |||
exports[`should render correctly: project 1`] = ` | |||
<FacetBox | |||
property="createdAt" | |||
> | |||
<FacetHeader | |||
fetching={false} | |||
name="issues.facet.createdAt" | |||
onClear={[Function]} | |||
onClick={[Function]} | |||
open={true} | |||
values={[]} | |||
/> | |||
<div> | |||
<div | |||
className="search-navigator-date-facet-selection" | |||
> | |||
<DateRangeInput | |||
alignEndDateCalandarRight={true} | |||
onChange={[Function]} | |||
value={ | |||
{ | |||
"from": undefined, | |||
"to": undefined, | |||
} | |||
} | |||
/> | |||
</div> | |||
<div | |||
className="spacer-top issues-predefined-periods" | |||
> | |||
<FacetItem | |||
active={true} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.all" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.all" | |||
value="" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.last_week" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.last_week" | |||
value="1w" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.last_month" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.last_month" | |||
value="1m" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
loading={false} | |||
name="issues.facet.createdAt.last_year" | |||
onClick={[Function]} | |||
tooltip="issues.facet.createdAt.last_year" | |||
value="1y" | |||
/> | |||
</div> | |||
</div> | |||
</FacetBox> | |||
`; |
@@ -1,55 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<ListStyleFacet | |||
facetHeader="issues.facet.directories" | |||
fetching={false} | |||
getFacetItemText={[Function]} | |||
getSearchResultKey={[Function]} | |||
getSearchResultText={[Function]} | |||
loadSearchResultCount={[Function]} | |||
maxInitialItems={15} | |||
maxItems={100} | |||
minSearchLength={3} | |||
onChange={[MockFunction]} | |||
onSearch={[Function]} | |||
onToggle={[MockFunction]} | |||
open={false} | |||
property="directories" | |||
query={{}} | |||
renderFacetItem={[Function]} | |||
renderSearchResult={[Function]} | |||
searchPlaceholder="search.search_for_directories" | |||
values={ | |||
[ | |||
"foo/", | |||
"bar/baz/", | |||
] | |||
} | |||
/> | |||
`; | |||
exports[`should render correctly 2`] = ` | |||
<React.Fragment> | |||
<QualifierIcon | |||
className="little-spacer-right" | |||
qualifier="DIR" | |||
/> | |||
<React.Fragment> | |||
<mark> | |||
foo | |||
</mark> | |||
/bar | |||
</React.Fragment> | |||
</React.Fragment> | |||
`; | |||
exports[`should render correctly 3`] = ` | |||
<React.Fragment> | |||
<QualifierIcon | |||
className="little-spacer-right" | |||
qualifier="DIR" | |||
/> | |||
foo/bar | |||
</React.Fragment> | |||
`; |
@@ -1,59 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<ListStyleFacet | |||
facetHeader="issues.facet.files" | |||
fetching={false} | |||
getFacetItemText={[Function]} | |||
getSearchResultKey={[Function]} | |||
getSearchResultText={[Function]} | |||
loadSearchResultCount={[Function]} | |||
maxInitialItems={15} | |||
maxItems={100} | |||
minSearchLength={3} | |||
onChange={[MockFunction]} | |||
onSearch={[Function]} | |||
onToggle={[MockFunction]} | |||
open={false} | |||
property="files" | |||
query={{}} | |||
renderFacetItem={[Function]} | |||
renderSearchResult={[Function]} | |||
searchPlaceholder="search.search_for_files" | |||
values={ | |||
[ | |||
"foo", | |||
"bar", | |||
] | |||
} | |||
/> | |||
`; | |||
exports[`should render correctly 2`] = ` | |||
<React.Fragment> | |||
<QualifierIcon | |||
className="little-spacer-right" | |||
qualifier="FIL" | |||
/> | |||
<React.Fragment> | |||
foo | |||
/ | |||
<React.Fragment> | |||
<mark> | |||
bar | |||
</mark> | |||
.js | |||
</React.Fragment> | |||
</React.Fragment> | |||
</React.Fragment> | |||
`; | |||
exports[`should render correctly 3`] = ` | |||
<React.Fragment> | |||
<QualifierIcon | |||
className="little-spacer-right" | |||
qualifier="FIL" | |||
/> | |||
fooUuid | |||
</React.Fragment> | |||
`; |
@@ -1,50 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`ListStyleFacet's renderers should include renderFacetItem 1`] = ` | |||
<span> | |||
<QualifierIcon | |||
className="little-spacer-right" | |||
qualifier="TRK" | |||
/> | |||
First Project Name | |||
</span> | |||
`; | |||
exports[`ListStyleFacet's renderers should include renderSearchResult 1`] = ` | |||
<React.Fragment> | |||
<QualifierIcon | |||
className="little-spacer-right" | |||
qualifier="TRK" | |||
/> | |||
<React.Fragment> | |||
<mark> | |||
First | |||
</mark> | |||
Project Name | |||
</React.Fragment> | |||
</React.Fragment> | |||
`; | |||
exports[`should render correctly 1`] = ` | |||
<ListStyleFacet | |||
facetHeader="issues.facet.projects" | |||
fetching={false} | |||
getFacetItemText={[Function]} | |||
getSearchResultKey={[Function]} | |||
getSearchResultText={[Function]} | |||
loadSearchResultCount={[Function]} | |||
maxInitialItems={15} | |||
maxItems={100} | |||
minSearchLength={2} | |||
onChange={[MockFunction]} | |||
onSearch={[Function]} | |||
onToggle={[MockFunction]} | |||
open={false} | |||
property="projects" | |||
query={{}} | |||
renderFacetItem={[Function]} | |||
renderSearchResult={[Function]} | |||
searchPlaceholder="search.search_for_projects" | |||
values={[]} | |||
/> | |||
`; |
@@ -1,30 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<ListStyleFacet | |||
facetHeader="issues.facet.rules" | |||
fetching={true} | |||
getFacetItemText={[Function]} | |||
getSearchResultKey={[Function]} | |||
getSearchResultText={[Function]} | |||
loadSearchResultCount={[Function]} | |||
maxInitialItems={15} | |||
maxItems={100} | |||
minSearchLength={2} | |||
onChange={[MockFunction]} | |||
onSearch={[Function]} | |||
onToggle={[MockFunction]} | |||
open={false} | |||
property="rules" | |||
query={{}} | |||
renderFacetItem={[Function]} | |||
renderSearchResult={[Function]} | |||
searchPlaceholder="search.search_for_rules" | |||
stats={{}} | |||
values={ | |||
[ | |||
"r1", | |||
] | |||
} | |||
/> | |||
`; |
@@ -1,210 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly: active facet 1`] = ` | |||
<FacetBox | |||
property="scopes" | |||
> | |||
<FacetHeader | |||
fetching={true} | |||
name="issues.facet.scopes" | |||
onClear={[Function]} | |||
onClick={[Function]} | |||
open={true} | |||
values={ | |||
[ | |||
"issue.scope.MAIN", | |||
] | |||
} | |||
/> | |||
<FacetItemsList> | |||
<FacetItem | |||
active={true} | |||
halfWidth={false} | |||
key="MAIN" | |||
loading={false} | |||
name={ | |||
<span | |||
className="display-flex-center" | |||
> | |||
<QualifierIcon | |||
aria-hidden={true} | |||
className="little-spacer-right" | |||
qualifier="FIL" | |||
/> | |||
issue.scope.MAIN | |||
</span> | |||
} | |||
onClick={[Function]} | |||
value="MAIN" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
key="TEST" | |||
loading={false} | |||
name={ | |||
<span | |||
className="display-flex-center" | |||
> | |||
<QualifierIcon | |||
aria-hidden={true} | |||
className="little-spacer-right" | |||
qualifier="UTS" | |||
/> | |||
issue.scope.TEST | |||
</span> | |||
} | |||
onClick={[Function]} | |||
value="TEST" | |||
/> | |||
</FacetItemsList> | |||
<MultipleSelectionHint | |||
options={0} | |||
values={1} | |||
/> | |||
</FacetBox> | |||
`; | |||
exports[`should render correctly: default 1`] = ` | |||
<FacetBox | |||
property="scopes" | |||
> | |||
<FacetHeader | |||
fetching={true} | |||
name="issues.facet.scopes" | |||
onClear={[Function]} | |||
onClick={[Function]} | |||
open={false} | |||
values={[]} | |||
/> | |||
</FacetBox> | |||
`; | |||
exports[`should render correctly: disabled facet 1`] = ` | |||
<FacetBox | |||
property="scopes" | |||
> | |||
<FacetHeader | |||
fetching={true} | |||
name="issues.facet.scopes" | |||
onClear={[Function]} | |||
onClick={[Function]} | |||
open={true} | |||
values={[]} | |||
/> | |||
<FacetItemsList> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
key="MAIN" | |||
loading={false} | |||
name={ | |||
<span | |||
className="display-flex-center" | |||
> | |||
<QualifierIcon | |||
aria-hidden={true} | |||
className="little-spacer-right" | |||
qualifier="FIL" | |||
/> | |||
issue.scope.MAIN | |||
</span> | |||
} | |||
onClick={[Function]} | |||
stat={0} | |||
value="MAIN" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
key="TEST" | |||
loading={false} | |||
name={ | |||
<span | |||
className="display-flex-center" | |||
> | |||
<QualifierIcon | |||
aria-hidden={true} | |||
className="little-spacer-right" | |||
qualifier="UTS" | |||
/> | |||
issue.scope.TEST | |||
</span> | |||
} | |||
onClick={[Function]} | |||
value="TEST" | |||
/> | |||
</FacetItemsList> | |||
<MultipleSelectionHint | |||
options={1} | |||
values={0} | |||
/> | |||
</FacetBox> | |||
`; | |||
exports[`should render correctly: open 1`] = ` | |||
<FacetBox | |||
property="scopes" | |||
> | |||
<FacetHeader | |||
fetching={true} | |||
name="issues.facet.scopes" | |||
onClear={[Function]} | |||
onClick={[Function]} | |||
open={true} | |||
values={[]} | |||
/> | |||
<FacetItemsList> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
key="MAIN" | |||
loading={false} | |||
name={ | |||
<span | |||
className="display-flex-center" | |||
> | |||
<QualifierIcon | |||
aria-hidden={true} | |||
className="little-spacer-right" | |||
qualifier="FIL" | |||
/> | |||
issue.scope.MAIN | |||
</span> | |||
} | |||
onClick={[Function]} | |||
value="MAIN" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
key="TEST" | |||
loading={false} | |||
name={ | |||
<span | |||
className="display-flex-center" | |||
> | |||
<QualifierIcon | |||
aria-hidden={true} | |||
className="little-spacer-right" | |||
qualifier="UTS" | |||
/> | |||
issue.scope.TEST | |||
</span> | |||
} | |||
onClick={[Function]} | |||
value="TEST" | |||
/> | |||
</FacetItemsList> | |||
<MultipleSelectionHint | |||
options={0} | |||
values={0} | |||
/> | |||
</FacetBox> | |||
`; |
@@ -1,127 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should not render developer nominative facets when asked not to 1`] = ` | |||
[ | |||
"TypeFacet", | |||
"SeverityFacet", | |||
"ScopeFacet", | |||
"ResolutionFacet", | |||
"StatusFacet", | |||
"StandardFacet", | |||
"injectIntl(CreationDateFacet)", | |||
"withLanguagesContext(LanguageFacet)", | |||
"RuleFacet", | |||
"TagFacet", | |||
"ProjectFacet", | |||
] | |||
`; | |||
exports[`should render facets for "APP" 1`] = ` | |||
[ | |||
"PeriodFilter", | |||
"TypeFacet", | |||
"SeverityFacet", | |||
"ScopeFacet", | |||
"ResolutionFacet", | |||
"StatusFacet", | |||
"StandardFacet", | |||
"injectIntl(CreationDateFacet)", | |||
"withLanguagesContext(LanguageFacet)", | |||
"RuleFacet", | |||
"TagFacet", | |||
"ProjectFacet", | |||
"AssigneeFacet", | |||
"AuthorFacet", | |||
] | |||
`; | |||
exports[`should render facets for "SVW" 1`] = ` | |||
[ | |||
"TypeFacet", | |||
"SeverityFacet", | |||
"ScopeFacet", | |||
"ResolutionFacet", | |||
"StatusFacet", | |||
"StandardFacet", | |||
"injectIntl(CreationDateFacet)", | |||
"withLanguagesContext(LanguageFacet)", | |||
"RuleFacet", | |||
"TagFacet", | |||
"ProjectFacet", | |||
"AssigneeFacet", | |||
"AuthorFacet", | |||
] | |||
`; | |||
exports[`should render facets for "VW" 1`] = ` | |||
[ | |||
"TypeFacet", | |||
"SeverityFacet", | |||
"ScopeFacet", | |||
"ResolutionFacet", | |||
"StatusFacet", | |||
"StandardFacet", | |||
"injectIntl(CreationDateFacet)", | |||
"withLanguagesContext(LanguageFacet)", | |||
"RuleFacet", | |||
"TagFacet", | |||
"ProjectFacet", | |||
"AssigneeFacet", | |||
"AuthorFacet", | |||
] | |||
`; | |||
exports[`should render facets for global page 1`] = ` | |||
[ | |||
"TypeFacet", | |||
"SeverityFacet", | |||
"ScopeFacet", | |||
"ResolutionFacet", | |||
"StatusFacet", | |||
"StandardFacet", | |||
"injectIntl(CreationDateFacet)", | |||
"withLanguagesContext(LanguageFacet)", | |||
"RuleFacet", | |||
"TagFacet", | |||
"ProjectFacet", | |||
"AssigneeFacet", | |||
"AuthorFacet", | |||
] | |||
`; | |||
exports[`should render facets for project 1`] = ` | |||
[ | |||
"PeriodFilter", | |||
"TypeFacet", | |||
"SeverityFacet", | |||
"ScopeFacet", | |||
"ResolutionFacet", | |||
"StatusFacet", | |||
"StandardFacet", | |||
"injectIntl(CreationDateFacet)", | |||
"withLanguagesContext(LanguageFacet)", | |||
"RuleFacet", | |||
"TagFacet", | |||
"DirectoryFacet", | |||
"FileFacet", | |||
"AssigneeFacet", | |||
"AuthorFacet", | |||
] | |||
`; | |||
exports[`should render facets when my issues are selected 1`] = ` | |||
[ | |||
"TypeFacet", | |||
"SeverityFacet", | |||
"ScopeFacet", | |||
"ResolutionFacet", | |||
"StatusFacet", | |||
"StandardFacet", | |||
"injectIntl(CreationDateFacet)", | |||
"withLanguagesContext(LanguageFacet)", | |||
"RuleFacet", | |||
"TagFacet", | |||
"ProjectFacet", | |||
"AuthorFacet", | |||
] | |||
`; |
@@ -1,180 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render closed 1`] = ` | |||
<FacetBox | |||
property="standards" | |||
> | |||
<FacetHeader | |||
name="issues.facet.standards" | |||
onClear={[Function]} | |||
onClick={[Function]} | |||
open={false} | |||
values={[]} | |||
/> | |||
</FacetBox> | |||
`; | |||
exports[`should render empty sub-facet 1`] = ` | |||
<FacetBox | |||
className="is-inner" | |||
property="owaspTop10-2021" | |||
> | |||
<FacetHeader | |||
fetching={false} | |||
name="issues.facet.owaspTop10_2021" | |||
onClick={[Function]} | |||
open={true} | |||
values={[]} | |||
/> | |||
<div | |||
className="search-navigator-facet-empty little-spacer-top" | |||
> | |||
no_results | |||
</div> | |||
<MultipleSelectionHint | |||
options={0} | |||
values={0} | |||
/> | |||
</FacetBox> | |||
`; | |||
exports[`should render sub-facets 1`] = ` | |||
<FacetBox | |||
property="standards" | |||
> | |||
<FacetHeader | |||
name="issues.facet.standards" | |||
onClear={[Function]} | |||
onClick={[Function]} | |||
open={true} | |||
values={ | |||
[ | |||
"SONAR SQL Injection", | |||
"OWASP A3", | |||
"CWE-42 - cwe-42 title", | |||
] | |||
} | |||
/> | |||
<FacetBox | |||
className="is-inner" | |||
property="sonarsourceSecurity" | |||
> | |||
<FacetHeader | |||
fetching={false} | |||
name="issues.facet.sonarsourceSecurity" | |||
onClick={[Function]} | |||
open={true} | |||
values={ | |||
[ | |||
"SQL Injection", | |||
] | |||
} | |||
/> | |||
<FacetItemsList> | |||
<FacetItem | |||
active={true} | |||
halfWidth={false} | |||
key="sql-injection" | |||
loading={false} | |||
name="SQL Injection" | |||
onClick={[Function]} | |||
stat="12" | |||
tooltip="SQL Injection" | |||
value="sql-injection" | |||
/> | |||
</FacetItemsList> | |||
<MultipleSelectionHint | |||
options={1} | |||
values={1} | |||
/> | |||
</FacetBox> | |||
<FacetBox | |||
className="is-inner" | |||
property="owaspTop10-2021" | |||
> | |||
<FacetHeader | |||
fetching={false} | |||
name="issues.facet.owaspTop10_2021" | |||
onClick={[Function]} | |||
open={false} | |||
values={[]} | |||
/> | |||
</FacetBox> | |||
<FacetBox | |||
className="is-inner" | |||
property="owaspTop10" | |||
> | |||
<FacetHeader | |||
fetching={false} | |||
name="issues.facet.owaspTop10" | |||
onClick={[Function]} | |||
open={true} | |||
values={ | |||
[ | |||
"A3", | |||
] | |||
} | |||
/> | |||
<FacetItemsList> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
key="a1" | |||
loading={false} | |||
name="A1 - a1 title" | |||
onClick={[Function]} | |||
stat="15" | |||
tooltip="A1 - a1 title" | |||
value="a1" | |||
/> | |||
<FacetItem | |||
active={true} | |||
halfWidth={false} | |||
key="a3" | |||
loading={false} | |||
name="A3" | |||
onClick={[Function]} | |||
stat="5" | |||
tooltip="A3" | |||
value="a3" | |||
/> | |||
</FacetItemsList> | |||
<MultipleSelectionHint | |||
options={2} | |||
values={1} | |||
/> | |||
</FacetBox> | |||
<ListStyleFacet | |||
className="is-inner" | |||
facetHeader="issues.facet.cwe" | |||
fetching={false} | |||
getFacetItemText={[Function]} | |||
getSearchResultKey={[Function]} | |||
getSearchResultText={[Function]} | |||
loadSearchResultCount={[Function]} | |||
maxInitialItems={15} | |||
maxItems={100} | |||
minSearchLength={2} | |||
onChange={[MockFunction]} | |||
onSearch={[Function]} | |||
onToggle={[MockFunction]} | |||
open={true} | |||
property="cwe" | |||
query={{}} | |||
renderFacetItem={[Function]} | |||
renderSearchResult={[Function]} | |||
searchPlaceholder="search.search_for_cwe" | |||
stats={ | |||
{ | |||
"173": 3, | |||
"42": 5, | |||
} | |||
} | |||
values={ | |||
[ | |||
"42", | |||
] | |||
} | |||
/> | |||
</FacetBox> | |||
`; |
@@ -1,97 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render correctly 1`] = ` | |||
<FacetBox | |||
property="statuses" | |||
> | |||
<FacetHeader | |||
fetching={false} | |||
name="issues.facet.statuses" | |||
onClear={[Function]} | |||
onClick={[Function]} | |||
open={true} | |||
values={[]} | |||
/> | |||
<FacetItemsList> | |||
<FacetItem | |||
active={false} | |||
halfWidth={true} | |||
key="OPEN" | |||
loading={false} | |||
name={ | |||
<StatusHelper | |||
status="OPEN" | |||
/> | |||
} | |||
onClick={[Function]} | |||
stat="104" | |||
tooltip="issue.status.OPEN" | |||
value="OPEN" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={true} | |||
key="CONFIRMED" | |||
loading={false} | |||
name={ | |||
<StatusHelper | |||
status="CONFIRMED" | |||
/> | |||
} | |||
onClick={[Function]} | |||
stat="8" | |||
tooltip="issue.status.CONFIRMED" | |||
value="CONFIRMED" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={true} | |||
key="REOPENED" | |||
loading={false} | |||
name={ | |||
<StatusHelper | |||
status="REOPENED" | |||
/> | |||
} | |||
onClick={[Function]} | |||
stat={0} | |||
tooltip="issue.status.REOPENED" | |||
value="REOPENED" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={true} | |||
key="RESOLVED" | |||
loading={false} | |||
name={ | |||
<StatusHelper | |||
status="RESOLVED" | |||
/> | |||
} | |||
onClick={[Function]} | |||
stat={0} | |||
tooltip="issue.status.RESOLVED" | |||
value="RESOLVED" | |||
/> | |||
<FacetItem | |||
active={false} | |||
halfWidth={true} | |||
key="CLOSED" | |||
loading={false} | |||
name={ | |||
<StatusHelper | |||
status="CLOSED" | |||
/> | |||
} | |||
onClick={[Function]} | |||
stat="8" | |||
tooltip="issue.status.CLOSED" | |||
value="CLOSED" | |||
/> | |||
</FacetItemsList> | |||
<MultipleSelectionHint | |||
options={5} | |||
values={0} | |||
/> | |||
</FacetBox> | |||
`; |
@@ -1,90 +0,0 @@ | |||
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||
exports[`should render open by default 1`] = ` | |||
<FacetBox | |||
property="types" | |||
> | |||
<FacetHeader | |||
fetching={false} | |||
name="issues.facet.types" | |||
onClear={[Function]} | |||
onClick={[Function]} | |||
open={true} | |||
values={ | |||
[ | |||
"issue.type.VULNERABILITY", | |||
"issue.type.CODE_SMELL", | |||
] | |||
} | |||
/> | |||
<FacetItemsList> | |||
<FacetItem | |||
active={false} | |||
halfWidth={false} | |||
key="BUG" | |||
loading={false} | |||
name={ | |||
<span | |||
className="display-flex-center" | |||
> | |||
<IssueTypeIcon | |||
className="little-spacer-right" | |||
query="BUG" | |||
/> | |||
issue.type.BUG | |||
</span> | |||
} | |||
onClick={[Function]} | |||
stat={0} | |||
value="BUG" | |||
/> | |||
<FacetItem | |||
active={true} | |||
halfWidth={false} | |||
key="VULNERABILITY" | |||
loading={false} | |||
name={ | |||
<span | |||
className="display-flex-center" | |||
> | |||
<IssueTypeIcon | |||
className="little-spacer-right" | |||
query="VULNERABILITY" | |||
/> | |||
issue.type.VULNERABILITY | |||
</span> | |||
} | |||
onClick={[Function]} | |||
stat="2" | |||
value="VULNERABILITY" | |||
/> | |||
<FacetItem | |||
active={true} | |||
halfWidth={false} | |||
key="CODE_SMELL" | |||
loading={false} | |||
name={ | |||
<span | |||
className="display-flex-center" | |||
> | |||
<IssueTypeIcon | |||
className="little-spacer-right" | |||
query="CODE_SMELL" | |||
/> | |||
issue.type.CODE_SMELL | |||
</span> | |||
} | |||
onClick={[Function]} | |||
stat="5" | |||
value="CODE_SMELL" | |||
/> | |||
</FacetItemsList> | |||
<MultipleSelectionHint | |||
options={4} | |||
values={2} | |||
/> | |||
</FacetBox> | |||
`; |
@@ -93,6 +93,7 @@ exports[`should render correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -128,6 +129,7 @@ exports[`should render correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", |
@@ -33,6 +33,7 @@ exports[`should render issues 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", |
@@ -27,6 +27,7 @@ exports[`should render hotspots correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -64,6 +65,7 @@ exports[`should render hotspots correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -125,6 +127,7 @@ exports[`should render issues correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -176,6 +179,7 @@ exports[`should render issues correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", |
@@ -37,6 +37,7 @@ exports[`should render issues correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", |
@@ -20,7 +20,7 @@ | |||
import classNames from 'classnames'; | |||
import * as React from 'react'; | |||
import { translate, translateWithParameters } from '../../../helpers/l10n'; | |||
import { IssueResponse } from '../../../types/issues'; | |||
import { IssueResolution, IssueResponse, IssueType as IssueTypeEnum } from '../../../types/issues'; | |||
import { Issue, RawQuery } from '../../../types/types'; | |||
import { updateIssue } from '../actions'; | |||
import IssueAssign from './IssueAssign'; | |||
@@ -81,8 +81,8 @@ export default class IssueActionsBar extends React.PureComponent<Props, State> { | |||
handleTransition = (issue: Issue) => { | |||
this.props.onChange(issue); | |||
if ( | |||
issue.resolution === 'FALSE-POSITIVE' || | |||
(issue.resolution === 'WONTFIX' && issue.type !== 'SECURITY_HOTSPOT') | |||
issue.resolution === IssueResolution.FalsePositive || | |||
(issue.resolution === IssueResolution.WontFix && issue.type !== IssueTypeEnum.SecurityHotspot) | |||
) { | |||
this.toggleComment(true, translate('issue.comment.explain_why'), true); | |||
} | |||
@@ -96,7 +96,7 @@ export default class IssueActionsBar extends React.PureComponent<Props, State> { | |||
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 === 'SECURITY_HOTSPOT'; | |||
const isSecurityHotspot = issue.type === IssueTypeEnum.SecurityHotspot; | |||
return ( | |||
<div className={classNames(className, 'issue-actions')}> |
@@ -34,6 +34,7 @@ exports[`should render commentable correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -78,6 +79,7 @@ exports[`should render commentable correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -122,6 +124,7 @@ exports[`should render commentable correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -166,6 +169,7 @@ exports[`should render commentable correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -222,6 +226,7 @@ exports[`should render commentable correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -276,6 +281,7 @@ exports[`should render effort correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -319,6 +325,7 @@ exports[`should render effort correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -362,6 +369,7 @@ exports[`should render effort correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -405,6 +413,7 @@ exports[`should render effort correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -461,6 +470,7 @@ exports[`should render effort correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -514,6 +524,7 @@ exports[`should render issue correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -556,6 +567,7 @@ exports[`should render issue correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -598,6 +610,7 @@ exports[`should render issue correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -640,6 +653,7 @@ exports[`should render issue correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -686,6 +700,7 @@ exports[`should render issue correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -739,6 +754,7 @@ exports[`should render security hotspot correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -781,6 +797,7 @@ exports[`should render security hotspot correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -823,6 +840,7 @@ exports[`should render security hotspot correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -869,6 +887,7 @@ exports[`should render security hotspot correctly 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", |
@@ -25,6 +25,7 @@ exports[`should render correctly: default 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -71,6 +72,7 @@ exports[`should render correctly: default 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -146,6 +148,7 @@ exports[`should render correctly: with filter 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -192,6 +195,7 @@ exports[`should render correctly: with filter 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -262,6 +266,7 @@ exports[`should render correctly: with filter 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [], | |||
"severity": "MAJOR", | |||
"status": "OPEN", | |||
@@ -358,6 +363,7 @@ exports[`should render correctly: with multi locations 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [ | |||
{ | |||
"component": "main.js", | |||
@@ -472,6 +478,7 @@ exports[`should render correctly: with multi locations 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [ | |||
{ | |||
"component": "main.js", | |||
@@ -634,6 +641,7 @@ exports[`should render correctly: with multi locations and link 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [ | |||
{ | |||
"component": "main.js", | |||
@@ -748,6 +756,7 @@ exports[`should render correctly: with multi locations and link 1`] = ` | |||
"projectName": "Foo", | |||
"rule": "javascript:S1067", | |||
"ruleName": "foo", | |||
"scope": "MAIN", | |||
"secondaryLocations": [ | |||
{ | |||
"component": "main.js", |
@@ -17,6 +17,7 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import { Query } from '../../apps/issues/utils'; | |||
import { ReferencedRule } from '../../types/issues'; | |||
import { IssueChangelog } from '../../types/types'; | |||
@@ -44,3 +45,39 @@ export function mockIssueChangelog(overrides: Partial<IssueChangelog> = {}): Iss | |||
...overrides, | |||
}; | |||
} | |||
export function mockQuery(overrides: Partial<Query> = {}): Query { | |||
return { | |||
assigned: false, | |||
assignees: [], | |||
author: [], | |||
createdAfter: undefined, | |||
createdAt: '', | |||
createdBefore: undefined, | |||
createdInLast: '', | |||
cwe: [], | |||
directories: [], | |||
files: [], | |||
issues: [], | |||
languages: [], | |||
owaspTop10: [], | |||
'owaspTop10-2021': [], | |||
'pciDss-3.2': [], | |||
'pciDss-4.0': [], | |||
'owaspAsvs-4.0': [], | |||
owaspAsvsLevel: '', | |||
projects: [], | |||
resolutions: [], | |||
resolved: false, | |||
rules: [], | |||
scopes: [], | |||
severities: [], | |||
inNewCodePeriod: false, | |||
sonarsourceSecurity: [], | |||
sort: '', | |||
statuses: [], | |||
tags: [], | |||
types: [], | |||
...overrides, | |||
}; | |||
} |
@@ -25,7 +25,7 @@ import { Location, Router } from '../components/hoc/withRouter'; | |||
import { AppState } from '../types/appstate'; | |||
import { RuleRepository } from '../types/coding-rules'; | |||
import { EditionKey } from '../types/editions'; | |||
import { IssueType, RawIssue } from '../types/issues'; | |||
import { IssueScope, IssueSeverity, IssueStatus, IssueType, RawIssue } from '../types/issues'; | |||
import { Language } from '../types/languages'; | |||
import { DumpStatus, DumpTask } from '../types/project-dump'; | |||
import { TaskStatuses } from '../types/tasks'; | |||
@@ -286,13 +286,15 @@ export function mockRawIssue(withLocations = false, overrides: Partial<RawIssue> | |||
actions: [], | |||
component: 'main.js', | |||
key: 'AVsae-CQS-9G3txfbFN2', | |||
creationDate: '2023-01-15T09:36:01+0100', | |||
line: 25, | |||
project: 'myproject', | |||
rule: 'javascript:S1067', | |||
severity: 'MAJOR', | |||
status: 'OPEN', | |||
severity: IssueSeverity.Major, | |||
status: IssueStatus.Open, | |||
textRange: { startLine: 25, endLine: 26, startOffset: 0, endOffset: 15 }, | |||
type: IssueType.CodeSmell, | |||
scope: IssueScope.Main, | |||
...overrides, | |||
}; | |||
@@ -334,9 +336,10 @@ export function mockIssue(withLocations = false, overrides: Partial<Issue> = {}) | |||
projectName: 'Foo', | |||
rule: 'javascript:S1067', | |||
ruleName: 'foo', | |||
scope: IssueScope.Main, | |||
secondaryLocations: [], | |||
severity: 'MAJOR', | |||
status: 'OPEN', | |||
severity: IssueSeverity.Major, | |||
status: IssueStatus.Open, | |||
textRange: { startLine: 25, endLine: 26, startOffset: 0, endOffset: 15 }, | |||
transitions: [], | |||
type: 'BUG', |
@@ -29,11 +29,27 @@ export enum IssueType { | |||
SecurityHotspot = 'SECURITY_HOTSPOT', | |||
} | |||
export enum IssueSeverity { | |||
Blocker = 'BLOCKER', | |||
Minor = 'MINOR', | |||
Critical = 'CRITICAL', | |||
Info = 'INFO', | |||
Major = 'MAJOR', | |||
} | |||
export enum IssueScope { | |||
Main = 'MAIN', | |||
Test = 'TEST', | |||
} | |||
export enum IssueResolution { | |||
Unresolved = '', | |||
FalsePositive = 'FALSE-POSITIVE', | |||
Fixed = 'FIXED', | |||
Removed = 'REMOVED', | |||
WontFix = 'WONTFIX', | |||
} | |||
export enum IssueStatus { | |||
Open = 'OPEN', | |||
Confirmed = 'CONFIRMED', | |||
@@ -76,6 +92,7 @@ export interface RawIssue { | |||
assignee?: string; | |||
author?: string; | |||
comments?: Array<Comment>; | |||
creationDate: string; | |||
component: string; | |||
flows?: Array<{ | |||
type?: string; | |||
@@ -93,6 +110,7 @@ export interface RawIssue { | |||
status: string; | |||
textRange?: TextRange; | |||
type: IssueType; | |||
scope: string; | |||
ruleDescriptionContextKey?: string; | |||
ruleStatus?: string; | |||
quickFixAvailable?: boolean; |
@@ -268,6 +268,7 @@ export interface Issue { | |||
ruleDescriptionContextKey?: string; | |||
ruleName: string; | |||
ruleStatus?: string; | |||
scope: string; | |||
secondaryLocations: FlowLocation[]; | |||
severity: string; | |||
status: string; |