constructor() { | constructor() { | ||||
this.defaultList = [ | this.defaultList = [ | ||||
{ | |||||
issue: mockRawIssue(false, { | |||||
key: 'issue101', | |||||
component: 'foo:test1.js', | |||||
message: 'Issue with no location message', | |||||
rule: 'simpleRuleId', | |||||
textRange: { | |||||
startLine: 10, | |||||
endLine: 10, | |||||
startOffset: 0, | |||||
endOffset: 2, | |||||
}, | |||||
flows: [ | |||||
{ | |||||
locations: [ | |||||
{ | |||||
component: 'foo:test1.js', | |||||
textRange: { | |||||
startLine: 1, | |||||
endLine: 1, | |||||
startOffset: 0, | |||||
endOffset: 1, | |||||
}, | |||||
}, | |||||
], | |||||
}, | |||||
{ | |||||
locations: [ | |||||
{ | |||||
component: 'foo:test2.js', | |||||
textRange: { | |||||
startLine: 20, | |||||
endLine: 20, | |||||
startOffset: 0, | |||||
endOffset: 1, | |||||
}, | |||||
}, | |||||
], | |||||
}, | |||||
], | |||||
}), | |||||
snippets: keyBy( | |||||
[ | |||||
mockSnippetsByComponent( | |||||
'test1.js', | |||||
'foo', | |||||
times(40, (i) => i + 1) | |||||
), | |||||
mockSnippetsByComponent( | |||||
'test2.js', | |||||
'foo', | |||||
times(40, (i) => i + 1) | |||||
), | |||||
], | |||||
'component.key' | |||||
), | |||||
}, | |||||
{ | { | ||||
issue: mockRawIssue(false, { | issue: mockRawIssue(false, { | ||||
key: 'issue11', | key: 'issue11', |
expect(await screen.findByRole('alert', { name: 'alert.tooltip.warning' })).toBeInTheDocument(); | expect(await screen.findByRole('alert', { name: 'alert.tooltip.warning' })).toBeInTheDocument(); | ||||
}); | }); | ||||
it('should show secondary location even when no message is present', async () => { | |||||
renderProjectIssuesApp('project/issues?issues=issue101&open=issue101&id=myproject'); | |||||
expect(await screen.findByRole('button', { name: '1 issue.location_x.1' })).toBeInTheDocument(); | |||||
expect(screen.getByRole('button', { name: '2 issue.location_x.2' })).toBeInTheDocument(); | |||||
}); | |||||
it('should interact with flows and locations', async () => { | it('should interact with flows and locations', async () => { | ||||
const user = userEvent.setup(); | const user = userEvent.setup(); | ||||
renderProjectIssuesApp('project/issues?issues=issue11&open=issue11&id=myproject'); | renderProjectIssuesApp('project/issues?issues=issue11&open=issue11&id=myproject'); |
const locationComponents = [componentKey, ...locations.map((location) => location.component)]; | const locationComponents = [componentKey, ...locations.map((location) => location.component)]; | ||||
const isCrossFile = uniq(locationComponents).length > 1; | const isCrossFile = uniq(locationComponents).length > 1; | ||||
if (!locations || locations.length === 0 || locations.every((location) => !location.msg)) { | |||||
if (!locations || locations.length === 0) { | |||||
return null; | return null; | ||||
} | } | ||||
*/ | */ | ||||
import classNames from 'classnames'; | import classNames from 'classnames'; | ||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { translateWithParameters } from '../../helpers/l10n'; | |||||
import { MessageFormatting } from '../../types/issues'; | import { MessageFormatting } from '../../types/issues'; | ||||
import LocationIndex from '../common/LocationIndex'; | import LocationIndex from '../common/LocationIndex'; | ||||
import LocationMessage from '../common/LocationMessage'; | import LocationMessage from '../common/LocationMessage'; | ||||
> | > | ||||
<LocationIndex>{index + 1}</LocationIndex> | <LocationIndex>{index + 1}</LocationIndex> | ||||
<LocationMessage> | <LocationMessage> | ||||
{<IssueMessageHighlighting message={message} messageFormattings={messageFormattings} />} | |||||
{message ? ( | |||||
<IssueMessageHighlighting message={message} messageFormattings={messageFormattings} /> | |||||
) : ( | |||||
translateWithParameters('issue.location_x', index + 1) | |||||
)} | |||||
</LocationMessage> | </LocationMessage> | ||||
</ButtonPlain> | </ButtonPlain> | ||||
); | ); |
/* | |||||
* 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 SingleFileLocationNavigator from '../SingleFileLocationNavigator'; | |||||
it('should render correctly', () => { | |||||
expect(shallowRender()).toMatchSnapshot('index 1'); | |||||
expect(shallowRender({ index: 1 })).toMatchSnapshot('index 2'); | |||||
}); | |||||
function shallowRender(props: Partial<SingleFileLocationNavigator['props']> = {}) { | |||||
return shallow( | |||||
<SingleFileLocationNavigator | |||||
index={0} | |||||
message="" | |||||
onClick={jest.fn()} | |||||
selected={true} | |||||
{...props} | |||||
/> | |||||
); | |||||
} |
// Jest Snapshot v1, https://goo.gl/fbAQLP | |||||
exports[`should render correctly: index 1 1`] = ` | |||||
<ButtonPlain | |||||
aria-current="location" | |||||
className="locations-navigator selected" | |||||
innerRef={[Function]} | |||||
onClick={[Function]} | |||||
preventDefault={true} | |||||
stopPropagation={true} | |||||
> | |||||
<LocationIndex> | |||||
1 | |||||
</LocationIndex> | |||||
<LocationMessage> | |||||
<IssueMessageHighlighting | |||||
message="" | |||||
/> | |||||
</LocationMessage> | |||||
</ButtonPlain> | |||||
`; | |||||
exports[`should render correctly: index 2 1`] = ` | |||||
<ButtonPlain | |||||
aria-current="location" | |||||
className="locations-navigator selected" | |||||
innerRef={[Function]} | |||||
onClick={[Function]} | |||||
preventDefault={true} | |||||
stopPropagation={true} | |||||
> | |||||
<LocationIndex> | |||||
2 | |||||
</LocationIndex> | |||||
<LocationMessage> | |||||
<IssueMessageHighlighting | |||||
message="" | |||||
/> | |||||
</LocationMessage> | |||||
</ButtonPlain> | |||||
`; |
issue.tabs.code=Where is the issue? | issue.tabs.code=Where is the issue? | ||||
issue.x_data_flows={0} data flow(s) | issue.x_data_flows={0} data flow(s) | ||||
issue.execution_flow=Full execution flow | issue.execution_flow=Full execution flow | ||||
issue.location_x=Location {0} | |||||
issue.closed.file_level=This issue is {status}. It was detected in the file below and is no longer being detected. | issue.closed.file_level=This issue is {status}. It was detected in the file below and is no longer being detected. | ||||
issue.closed.project_level=This issue is {status}. It was detected in the project below and is no longer being detected. | issue.closed.project_level=This issue is {status}. It was detected in the project below and is no longer being detected. | ||||