@@ -25,6 +25,7 @@ import { connect } from 'react-redux'; | |||
import { withRouter, WithRouterProps } from 'react-router'; | |||
import ListFooter from 'sonar-ui-common/components/controls/ListFooter'; | |||
import SearchBox from 'sonar-ui-common/components/controls/SearchBox'; | |||
import BackIcon from 'sonar-ui-common/components/icons/BackIcon'; | |||
import { translate } from 'sonar-ui-common/helpers/l10n'; | |||
import { | |||
addSideBarClass, | |||
@@ -65,6 +66,7 @@ import { | |||
getAppFacet, | |||
getOpen, | |||
getServerFacet, | |||
hasRuleKey, | |||
OpenFacets, | |||
parseQuery, | |||
Query, | |||
@@ -107,6 +109,7 @@ interface State { | |||
referencedRepositories: T.Dict<{ key: string; language: string; name: string }>; | |||
rules: T.Rule[]; | |||
selected?: string; | |||
usingPermalink?: boolean; | |||
} | |||
export class App extends React.PureComponent<Props, State> { | |||
@@ -145,6 +148,7 @@ export class App extends React.PureComponent<Props, State> { | |||
const openRule = this.getOpenRule(nextProps, rules); | |||
return { | |||
openRule, | |||
usingPermalink: hasRuleKey(nextProps.location.query), | |||
query: parseQuery(nextProps.location.query), | |||
selected: openRule ? openRule.key : selected | |||
}; | |||
@@ -187,7 +191,7 @@ export class App extends React.PureComponent<Props, State> { | |||
return false; | |||
}); | |||
key('left', 'coding-rules', () => { | |||
this.closeRule(); | |||
this.handleBack(); | |||
return false; | |||
}); | |||
}; | |||
@@ -271,8 +275,18 @@ export class App extends React.PureComponent<Props, State> { | |||
this.makeFetchRequest(query).then(({ actives, facets, paging, rules }) => { | |||
if (this.mounted) { | |||
const openRule = this.getOpenRule(this.props, rules); | |||
const usingPermalink = hasRuleKey(this.props.location.query); | |||
const selected = rules.length > 0 ? (openRule && openRule.key) || rules[0].key : undefined; | |||
this.setState({ actives, facets, loading: false, openRule, paging, rules, selected }); | |||
this.setState({ | |||
actives, | |||
facets, | |||
loading: false, | |||
openRule, | |||
paging, | |||
rules, | |||
selected, | |||
usingPermalink | |||
}); | |||
} | |||
}, this.stopLoading); | |||
}; | |||
@@ -422,10 +436,19 @@ export class App extends React.PureComponent<Props, State> { | |||
this.props.router.push(this.getRulePath(ruleKey)); | |||
}; | |||
handleBack = (event: React.SyntheticEvent<HTMLAnchorElement>) => { | |||
event.preventDefault(); | |||
event.currentTarget.blur(); | |||
this.closeRule(); | |||
handleBack = (event?: React.SyntheticEvent<HTMLAnchorElement>) => { | |||
const { usingPermalink } = this.state; | |||
if (event) { | |||
event.preventDefault(); | |||
event.currentTarget.blur(); | |||
} | |||
if (usingPermalink) { | |||
this.handleReset(); | |||
} else { | |||
this.closeRule(); | |||
} | |||
}; | |||
handleFilterChange = (changes: Partial<Query>) => { | |||
@@ -607,18 +630,26 @@ export class App extends React.PureComponent<Props, State> { | |||
<div className="layout-page-main-inner"> | |||
<A11ySkipTarget anchor="rules_main" /> | |||
{this.state.openRule ? ( | |||
<a className="js-back" href="#" onClick={this.handleBack}> | |||
{translate('coding_rules.return_to_list')} | |||
<a | |||
className="js-back display-inline-flex-center link-no-underline" | |||
href="#" | |||
onClick={this.handleBack}> | |||
<BackIcon className="spacer-right" /> | |||
{this.state.usingPermalink | |||
? translate('coding_rules.see_all') | |||
: translate('coding_rules.return_to_list')} | |||
</a> | |||
) : ( | |||
this.renderBulkButton() | |||
)} | |||
<PageActions | |||
loading={this.state.loading} | |||
onReload={this.handleReload} | |||
paging={paging} | |||
selectedIndex={selectedIndex} | |||
/> | |||
{!this.state.usingPermalink && ( | |||
<PageActions | |||
loading={this.state.loading} | |||
onReload={this.handleReload} | |||
paging={paging} | |||
selectedIndex={selectedIndex} | |||
/> | |||
)} | |||
</div> | |||
</div> | |||
</div> |
@@ -17,33 +17,40 @@ | |||
* along with this program; if not, write to the Free Software Foundation, | |||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |||
*/ | |||
import * as React from 'react'; | |||
import { shallow } from 'enzyme'; | |||
import * as React from 'react'; | |||
import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; | |||
import { App } from '../App'; | |||
import { getRulesApp } from '../../../../api/rules'; | |||
import ScreenPositionHelper from '../../../../components/common/ScreenPositionHelper'; | |||
import { isSonarCloud } from '../../../../helpers/system'; | |||
import { | |||
mockAppState, | |||
mockCurrentUser, | |||
mockLocation, | |||
mockOrganization, | |||
mockRouter | |||
mockRouter, | |||
mockRule | |||
} from '../../../../helpers/testMocks'; | |||
import { getRulesApp } from '../../../../api/rules'; | |||
import { isSonarCloud } from '../../../../helpers/system'; | |||
import { App } from '../App'; | |||
jest.mock('../../../../api/rules', () => ({ | |||
getRulesApp: jest.fn().mockResolvedValue({ canWrite: true, repositories: [] }), | |||
searchRules: jest.fn().mockResolvedValue({ | |||
actives: [], | |||
rawActives: [], | |||
facets: [], | |||
rawFacets: [], | |||
p: 0, | |||
ps: 100, | |||
rules: [], | |||
total: 0 | |||
}) | |||
})); | |||
jest.mock('../../../../components/common/ScreenPositionHelper'); | |||
jest.mock('../../../../api/rules', () => { | |||
const { mockRule } = jest.requireActual('../../../../helpers/testMocks'); | |||
return { | |||
getRulesApp: jest.fn().mockResolvedValue({ canWrite: true, repositories: [] }), | |||
searchRules: jest.fn().mockResolvedValue({ | |||
actives: [], | |||
rawActives: [], | |||
facets: [], | |||
rawFacets: [], | |||
p: 0, | |||
ps: 100, | |||
rules: [mockRule(), mockRule()], | |||
total: 0 | |||
}) | |||
}; | |||
}); | |||
jest.mock('../../../../api/quality-profiles', () => ({ | |||
searchQualityProfiles: jest.fn().mockResolvedValue({ profiles: [] }) | |||
@@ -55,10 +62,22 @@ jest.mock('../../../../helpers/system', () => ({ | |||
it('should render correctly', async () => { | |||
const wrapper = shallowRender(); | |||
expect(wrapper).toMatchSnapshot(); | |||
expect(wrapper).toMatchSnapshot('loading'); | |||
await waitAndUpdate(wrapper); | |||
expect(wrapper).toMatchSnapshot(); | |||
expect(wrapper).toMatchSnapshot('loaded'); | |||
expect(wrapper.find(ScreenPositionHelper).dive()).toMatchSnapshot( | |||
'loaded (ScreenPositionHelper)' | |||
); | |||
wrapper.setState({ openRule: mockRule() }); | |||
expect(wrapper).toMatchSnapshot('open rule'); | |||
expect(wrapper.find(ScreenPositionHelper).dive()).toMatchSnapshot( | |||
'open rule (ScreenPositionHelper)' | |||
); | |||
wrapper.setState({ usingPermalink: true }); | |||
expect(wrapper).toMatchSnapshot('using permalink'); | |||
}); | |||
describe('renderBulkButton', () => { |
@@ -39,7 +39,249 @@ exports[`renderBulkButton should show bulk change button when everything is fine | |||
/> | |||
`; | |||
exports[`should render correctly 1`] = ` | |||
exports[`should render correctly: loaded (ScreenPositionHelper) 1`] = ` | |||
<div | |||
className="layout-page-side" | |||
style={ | |||
Object { | |||
"top": 0, | |||
} | |||
} | |||
> | |||
<div | |||
className="layout-page-side-inner" | |||
> | |||
<div | |||
className="layout-page-filters" | |||
> | |||
<A11ySkipTarget | |||
anchor="rules_filters" | |||
label="coding_rules.skip_to_filters" | |||
weight={10} | |||
/> | |||
<FiltersHeader | |||
displayReset={false} | |||
onReset={[Function]} | |||
/> | |||
<SearchBox | |||
className="spacer-bottom" | |||
id="coding-rules-search" | |||
minLength={2} | |||
onChange={[Function]} | |||
placeholder="search.search_for_rules" | |||
value="" | |||
/> | |||
<FacetsList | |||
facets={Object {}} | |||
hideProfileFacet={false} | |||
onFacetToggle={[Function]} | |||
onFilterChange={[Function]} | |||
openFacets={ | |||
Object { | |||
"languages": true, | |||
"owaspTop10": false, | |||
"sansTop25": false, | |||
"sonarsourceSecurity": false, | |||
"standards": false, | |||
"types": true, | |||
} | |||
} | |||
organization="foo" | |||
query={ | |||
Object { | |||
"activation": undefined, | |||
"activationSeverities": Array [], | |||
"availableSince": undefined, | |||
"compareToProfile": undefined, | |||
"cwe": Array [], | |||
"inheritance": undefined, | |||
"languages": Array [], | |||
"owaspTop10": Array [], | |||
"profile": undefined, | |||
"repositories": Array [], | |||
"ruleKey": undefined, | |||
"sansTop25": Array [], | |||
"searchQuery": undefined, | |||
"severities": Array [], | |||
"sonarsourceSecurity": Array [], | |||
"statuses": Array [], | |||
"tags": Array [], | |||
"template": undefined, | |||
"types": Array [], | |||
} | |||
} | |||
referencedProfiles={Object {}} | |||
referencedRepositories={Object {}} | |||
/> | |||
</div> | |||
</div> | |||
</div> | |||
`; | |||
exports[`should render correctly: loaded 1`] = ` | |||
<Fragment> | |||
<Suggestions | |||
suggestions="coding_rules" | |||
/> | |||
<Helmet | |||
defer={false} | |||
encodeSpecialCharacters={true} | |||
title="coding_rules.page" | |||
> | |||
<meta | |||
content="noindex" | |||
name="robots" | |||
/> | |||
</Helmet> | |||
<div | |||
className="layout-page" | |||
id="coding-rules-page" | |||
> | |||
<ScreenPositionHelper | |||
className="layout-page-side-outer" | |||
> | |||
<Component /> | |||
</ScreenPositionHelper> | |||
<div | |||
className="layout-page-main" | |||
> | |||
<div | |||
className="layout-page-header-panel layout-page-main-header" | |||
> | |||
<div | |||
className="layout-page-header-panel-inner layout-page-main-header-inner" | |||
> | |||
<div | |||
className="layout-page-main-inner" | |||
> | |||
<A11ySkipTarget | |||
anchor="rules_main" | |||
/> | |||
<BulkChange | |||
languages={ | |||
Object { | |||
"js": Object { | |||
"key": "js", | |||
"name": "JavaScript", | |||
}, | |||
} | |||
} | |||
organization="foo" | |||
query={ | |||
Object { | |||
"activation": undefined, | |||
"activationSeverities": Array [], | |||
"availableSince": undefined, | |||
"compareToProfile": undefined, | |||
"cwe": Array [], | |||
"inheritance": undefined, | |||
"languages": Array [], | |||
"owaspTop10": Array [], | |||
"profile": undefined, | |||
"repositories": Array [], | |||
"ruleKey": undefined, | |||
"sansTop25": Array [], | |||
"searchQuery": undefined, | |||
"severities": Array [], | |||
"sonarsourceSecurity": Array [], | |||
"statuses": Array [], | |||
"tags": Array [], | |||
"template": undefined, | |||
"types": Array [], | |||
} | |||
} | |||
referencedProfiles={Object {}} | |||
total={0} | |||
/> | |||
<PageActions | |||
loading={false} | |||
onReload={[Function]} | |||
paging={ | |||
Object { | |||
"pageIndex": 0, | |||
"pageSize": 100, | |||
"total": 0, | |||
} | |||
} | |||
selectedIndex={0} | |||
/> | |||
</div> | |||
</div> | |||
</div> | |||
<div | |||
className="layout-page-main-inner" | |||
> | |||
<RuleListItem | |||
canWrite={true} | |||
isLoggedIn={true} | |||
key="javascript:S1067" | |||
onActivate={[Function]} | |||
onDeactivate={[Function]} | |||
onFilterChange={[Function]} | |||
onOpen={[Function]} | |||
organization="foo" | |||
rule={ | |||
Object { | |||
"key": "javascript:S1067", | |||
"lang": "js", | |||
"langName": "JavaScript", | |||
"name": "Use foo", | |||
"severity": "MAJOR", | |||
"status": "READY", | |||
"sysTags": Array [ | |||
"a", | |||
"b", | |||
], | |||
"tags": Array [ | |||
"x", | |||
], | |||
"type": "CODE_SMELL", | |||
} | |||
} | |||
selected={true} | |||
/> | |||
<RuleListItem | |||
canWrite={true} | |||
isLoggedIn={true} | |||
key="javascript:S1067" | |||
onActivate={[Function]} | |||
onDeactivate={[Function]} | |||
onFilterChange={[Function]} | |||
onOpen={[Function]} | |||
organization="foo" | |||
rule={ | |||
Object { | |||
"key": "javascript:S1067", | |||
"lang": "js", | |||
"langName": "JavaScript", | |||
"name": "Use foo", | |||
"severity": "MAJOR", | |||
"status": "READY", | |||
"sysTags": Array [ | |||
"a", | |||
"b", | |||
], | |||
"tags": Array [ | |||
"x", | |||
], | |||
"type": "CODE_SMELL", | |||
} | |||
} | |||
selected={true} | |||
/> | |||
<ListFooter | |||
count={2} | |||
loadMore={[Function]} | |||
ready={true} | |||
total={0} | |||
/> | |||
</div> | |||
</div> | |||
</div> | |||
</Fragment> | |||
`; | |||
exports[`should render correctly: loading 1`] = ` | |||
<Fragment> | |||
<Suggestions | |||
suggestions="coding_rules" | |||
@@ -93,7 +335,86 @@ exports[`should render correctly 1`] = ` | |||
</Fragment> | |||
`; | |||
exports[`should render correctly 2`] = ` | |||
exports[`should render correctly: open rule (ScreenPositionHelper) 1`] = ` | |||
<div | |||
className="layout-page-side" | |||
style={ | |||
Object { | |||
"top": 0, | |||
} | |||
} | |||
> | |||
<div | |||
className="layout-page-side-inner" | |||
> | |||
<div | |||
className="layout-page-filters" | |||
> | |||
<A11ySkipTarget | |||
anchor="rules_filters" | |||
label="coding_rules.skip_to_filters" | |||
weight={10} | |||
/> | |||
<FiltersHeader | |||
displayReset={false} | |||
onReset={[Function]} | |||
/> | |||
<SearchBox | |||
className="spacer-bottom" | |||
id="coding-rules-search" | |||
minLength={2} | |||
onChange={[Function]} | |||
placeholder="search.search_for_rules" | |||
value="" | |||
/> | |||
<FacetsList | |||
facets={Object {}} | |||
hideProfileFacet={false} | |||
onFacetToggle={[Function]} | |||
onFilterChange={[Function]} | |||
openFacets={ | |||
Object { | |||
"languages": true, | |||
"owaspTop10": false, | |||
"sansTop25": false, | |||
"sonarsourceSecurity": false, | |||
"standards": false, | |||
"types": true, | |||
} | |||
} | |||
organization="foo" | |||
query={ | |||
Object { | |||
"activation": undefined, | |||
"activationSeverities": Array [], | |||
"availableSince": undefined, | |||
"compareToProfile": undefined, | |||
"cwe": Array [], | |||
"inheritance": undefined, | |||
"languages": Array [], | |||
"owaspTop10": Array [], | |||
"profile": undefined, | |||
"repositories": Array [], | |||
"ruleKey": undefined, | |||
"sansTop25": Array [], | |||
"searchQuery": undefined, | |||
"severities": Array [], | |||
"sonarsourceSecurity": Array [], | |||
"statuses": Array [], | |||
"tags": Array [], | |||
"template": undefined, | |||
"types": Array [], | |||
} | |||
} | |||
referencedProfiles={Object {}} | |||
referencedRepositories={Object {}} | |||
/> | |||
</div> | |||
</div> | |||
</div> | |||
`; | |||
exports[`should render correctly: open rule 1`] = ` | |||
<Fragment> | |||
<Suggestions | |||
suggestions="coding_rules" | |||
@@ -132,42 +453,16 @@ exports[`should render correctly 2`] = ` | |||
<A11ySkipTarget | |||
anchor="rules_main" | |||
/> | |||
<BulkChange | |||
languages={ | |||
Object { | |||
"js": Object { | |||
"key": "js", | |||
"name": "JavaScript", | |||
}, | |||
} | |||
} | |||
organization="foo" | |||
query={ | |||
Object { | |||
"activation": undefined, | |||
"activationSeverities": Array [], | |||
"availableSince": undefined, | |||
"compareToProfile": undefined, | |||
"cwe": Array [], | |||
"inheritance": undefined, | |||
"languages": Array [], | |||
"owaspTop10": Array [], | |||
"profile": undefined, | |||
"repositories": Array [], | |||
"ruleKey": undefined, | |||
"sansTop25": Array [], | |||
"searchQuery": undefined, | |||
"severities": Array [], | |||
"sonarsourceSecurity": Array [], | |||
"statuses": Array [], | |||
"tags": Array [], | |||
"template": undefined, | |||
"types": Array [], | |||
} | |||
} | |||
referencedProfiles={Object {}} | |||
total={0} | |||
/> | |||
<a | |||
className="js-back display-inline-flex-center link-no-underline" | |||
href="#" | |||
onClick={[Function]} | |||
> | |||
<BackIcon | |||
className="spacer-right" | |||
/> | |||
coding_rules.return_to_list | |||
</a> | |||
<PageActions | |||
loading={false} | |||
onReload={[Function]} | |||
@@ -178,6 +473,7 @@ exports[`should render correctly 2`] = ` | |||
"total": 0, | |||
} | |||
} | |||
selectedIndex={0} | |||
/> | |||
</div> | |||
</div> | |||
@@ -185,11 +481,92 @@ exports[`should render correctly 2`] = ` | |||
<div | |||
className="layout-page-main-inner" | |||
> | |||
<ListFooter | |||
count={0} | |||
loadMore={[Function]} | |||
ready={true} | |||
total={0} | |||
<RuleDetails | |||
allowCustomRules={true} | |||
canWrite={true} | |||
hideQualityProfiles={false} | |||
onActivate={[Function]} | |||
onDeactivate={[Function]} | |||
onDelete={[Function]} | |||
onFilterChange={[Function]} | |||
organization="foo" | |||
referencedProfiles={Object {}} | |||
referencedRepositories={Object {}} | |||
ruleKey="javascript:S1067" | |||
/> | |||
</div> | |||
</div> | |||
</div> | |||
</Fragment> | |||
`; | |||
exports[`should render correctly: using permalink 1`] = ` | |||
<Fragment> | |||
<Suggestions | |||
suggestions="coding_rules" | |||
/> | |||
<Helmet | |||
defer={false} | |||
encodeSpecialCharacters={true} | |||
title="coding_rules.page" | |||
> | |||
<meta | |||
content="noindex" | |||
name="robots" | |||
/> | |||
</Helmet> | |||
<div | |||
className="layout-page" | |||
id="coding-rules-page" | |||
> | |||
<ScreenPositionHelper | |||
className="layout-page-side-outer" | |||
> | |||
<Component /> | |||
</ScreenPositionHelper> | |||
<div | |||
className="layout-page-main" | |||
> | |||
<div | |||
className="layout-page-header-panel layout-page-main-header" | |||
> | |||
<div | |||
className="layout-page-header-panel-inner layout-page-main-header-inner" | |||
> | |||
<div | |||
className="layout-page-main-inner" | |||
> | |||
<A11ySkipTarget | |||
anchor="rules_main" | |||
/> | |||
<a | |||
className="js-back display-inline-flex-center link-no-underline" | |||
href="#" | |||
onClick={[Function]} | |||
> | |||
<BackIcon | |||
className="spacer-right" | |||
/> | |||
coding_rules.see_all | |||
</a> | |||
</div> | |||
</div> | |||
</div> | |||
<div | |||
className="layout-page-main-inner" | |||
> | |||
<RuleDetails | |||
allowCustomRules={true} | |||
canWrite={true} | |||
hideQualityProfiles={false} | |||
onActivate={[Function]} | |||
onDeactivate={[Function]} | |||
onDelete={[Function]} | |||
onFilterChange={[Function]} | |||
organization="foo" | |||
referencedProfiles={Object {}} | |||
referencedRepositories={Object {}} | |||
ruleKey="javascript:S1067" | |||
/> | |||
</div> | |||
</div> |
@@ -156,6 +156,10 @@ export function getOpen(query: T.RawQuery) { | |||
return query.open; | |||
} | |||
export function hasRuleKey(query: T.RawQuery) { | |||
return Boolean(query.rule_key); | |||
} | |||
function parseAsInheritance(value?: string): T.RuleInheritance | undefined { | |||
if (value === 'INHERITED' || value === 'NONE' || value === 'OVERRIDES') { | |||
return value; |
@@ -26,16 +26,7 @@ import { isSonarCloud } from '../../../../helpers/system'; | |||
import getPages from '../../pages'; | |||
import App from '../App'; | |||
jest.mock('../../../../components/common/ScreenPositionHelper', () => ({ | |||
default: class ScreenPositionHelper extends React.Component<{ | |||
children: (pos: { top: number }) => React.ReactNode; | |||
}> { | |||
static displayName = 'ScreenPositionHelper'; | |||
render() { | |||
return this.props.children({ top: 0 }); | |||
} | |||
} | |||
})); | |||
jest.mock('../../../../components/common/ScreenPositionHelper'); | |||
jest.mock('../../../../helpers/system', () => ({ | |||
isSonarCloud: jest.fn().mockReturnValue(false) |
@@ -25,6 +25,8 @@ import { fetchWebApi } from '../../../../api/web-api'; | |||
import { mockLocation, mockRouter } from '../../../../helpers/testMocks'; | |||
import { WebApiApp } from '../WebApiApp'; | |||
jest.mock('../../../../components/common/ScreenPositionHelper'); | |||
jest.mock('../../../../api/web-api', () => ({ | |||
fetchWebApi: jest.fn().mockResolvedValue([ | |||
{ | |||
@@ -42,17 +44,6 @@ jest.mock('sonar-ui-common/helpers/pages', () => ({ | |||
removeSideBarClass: jest.fn() | |||
})); | |||
jest.mock('../../../../components/common/ScreenPositionHelper', () => ({ | |||
default: class ScreenPositionHelper extends React.Component<{ | |||
children: (pos: { top: number }) => React.ReactNode; | |||
}> { | |||
static displayName = 'ScreenPositionHelper'; | |||
render() { | |||
return this.props.children({ top: 0 }); | |||
} | |||
} | |||
})); | |||
it('should render correctly', async () => { | |||
(global as any).scrollTo = jest.fn(); | |||
@@ -0,0 +1,30 @@ | |||
/* | |||
* SonarQube | |||
* Copyright (C) 2009-2020 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 * as React from 'react'; | |||
interface Props { | |||
children: (position: { top: number }) => React.ReactNode; | |||
} | |||
export default class ScreenPositionHelper extends React.Component<Props> { | |||
render() { | |||
return this.props.children({ top: 0 }); | |||
} | |||
} |
@@ -1567,7 +1567,8 @@ coding_rules.quality_profiles.template_caption=This rule template was activated | |||
coding_rules.quality_profile=Quality Profile | |||
coding_rules.reactivate=Reactivate | |||
coding_rules.reactivate.help=A rule with the same key has been previously deleted. Please reactivate the existing rule or modify the key to create a new rule. | |||
coding_rules.return_to_list=Return to List | |||
coding_rules.return_to_list=Return to list | |||
coding_rules.see_all=See all rules | |||
coding_rules.remove_extended_description=Remove Extended Description | |||
coding_rules.remove_extended_description.confirm=Are you sure you want to remove the extended description? | |||
coding_rules.repository_language=Rule repository (language) |