Browse Source

SONAR-12500 Help section in the "Analysis scope" settings page should be more visible

tags/8.1.0.31237
Philippe Perrin 4 years ago
parent
commit
a631b1a1a4

+ 18
- 32
server/sonar-docs/src/pages/project-administration/narrowing-the-focus.md View File

@@ -14,6 +14,23 @@ If {instance}'s results aren't relevant, no one will want to use it. That's why

You can make these changes globally or at a project level. At both levels, the navigation path is the same: **Administration > General Settings > Analysis Scope**.

## Patterns

Paths are relative to the project base directory. The following wildcards can be used:

* `*` - Match zero or more characters
* `**` - Match zero or more directories
* `?` - Match a single character

Relative paths are based on the fully qualified name of the component.

Example|Matches|Does not match
----|----|----
`**/*Bean.java`|org/sonar.api/MyBean.java <br/> org/sonar/util/MyOtherBean.java|org/sonar/util/MyDTO.java
`**/*Bean?.java`|org/sonar/util/MyOtherBean1.java|org/sonar/util/MyOtherBean.java <br/> org/sonar.api/MyBean.java <br/> org/sonar/util/MyDTO.java
`org/sonar/*`|org/sonar/MyClass.java <br/> org/sonar/MyOtherClass.java|org/sonar/util/MyClassUtil.java
`org/sonar/**/*`|org/sonar/MyClass.java <br/> org/sonar/MyOtherClass.java <br/> org/sonar/util/MyClassUtil.java|

## Ignore Files
We recommend that you exclude generated code, source code from libraries, etc. There are four different ways to narrow your analysis to the source code that will be relevant to the development team. You can combine them all together to tune your analysis scope. Additionally, we automatically exclude from analysis the files described in your projects' `.gitignore` files. This behavior can be disabled. See `sonar.scm.exclusions.disabled` in the [Analysis Parameters](/analysis/analysis-parameters/) page for details.

@@ -116,35 +133,4 @@ To do so, go to **Administration > General Settings > Analysis Scope > Duplicati

You can prevent some files from being taken into account for code coverage by unit tests.

To do so, go to **Administration > General Settings > Analysis Scope > Code Coverage** and set the *Coverage Exclusions* property. See the Patterns section for more details on the syntax.

## Patterns

Paths are relative to the project base directory.

The following wildcards can be used:

* `*` - zero or more characters
* `**` - zero or more directories
* `?` - a single character

Relative paths are based on the fully qualified name of the component.

Examples:

`# Exclude all classes ending by "Bean"`
`# Matches org/sonar.api/MyBean.java, org/sonar/util/MyOtherBean.java, org/sonar/util/MyDTO.java, etc.`
`sonar.exclusions=**/*Bean.java,**/*DTO.java`

`# Exclude all classes in the "src/main/java/org/sonar" directory`
`# Matches src/main/java/org/sonar/MyClass.java, src/main/java/org/sonar/MyOtherClass.java`
`# But does not match src/main/java/org/sonar/util/MyClassUtil.java`
`sonar.exclusions=src/main/java/org/sonar/*`

`# Exclude all COBOL programs in the "bank" directory and its sub-directories`
`# Matches bank/ZTR00021.cbl, bank/data/CBR00354.cbl, bank/data/REM012345.cob`
`sonar.exclusions=bank/**/*`

`# Exclude all COBOL programs in the "bank" directory and its sub-directories whose extension is .cbl`
`# Matches bank/ZTR00021.cbl, bank/data/CBR00354.cbl`
`sonar.exclusions=bank/**/*.cbl`
To do so, go to **Administration > General Settings > Analysis Scope > Code Coverage** and set the *Coverage Exclusions* property. See the Patterns section for more details on the syntax.

+ 31
- 9
server/sonar-web/src/main/js/apps/settings/components/AdditionalCategories.tsx View File

@@ -20,19 +20,27 @@

import * as React from 'react';
import { translate } from 'sonar-ui-common/helpers/l10n';
import { LANGUAGES_CATEGORY, NEW_CODE_PERIOD_CATEGORY } from './AdditionalCategoryKeys';
import {
ANALYSIS_SCOPE_CATEGORY,
LANGUAGES_CATEGORY,
NEW_CODE_PERIOD_CATEGORY
} from './AdditionalCategoryKeys';
import { AnalysisScope } from './AnalysisScope';
import Languages from './Languages';
import NewCodePeriod from './NewCodePeriod';

export interface AdditionalCategoryComponentProps {
parentComponent: T.Component | undefined;
selectedCategory: string;
}

export interface AdditionalCategory {
key: string;
name: string;
renderComponent: (
parentComponent: T.Component | undefined,
selectedCategory: string
) => JSX.Element;
renderComponent: (props: AdditionalCategoryComponentProps) => JSX.Element;
availableGlobally: boolean;
availableForProject: boolean;
displayTab: boolean;
}

export const ADDITIONAL_CATEGORIES: AdditionalCategory[] = [
@@ -41,21 +49,35 @@ export const ADDITIONAL_CATEGORIES: AdditionalCategory[] = [
name: translate('property.category.languages'),
renderComponent: getLanguagesComponent,
availableGlobally: true,
availableForProject: true
availableForProject: true,
displayTab: true
},
{
key: NEW_CODE_PERIOD_CATEGORY,
name: translate('settings.new_code_period.category'),
renderComponent: getNewCodePeriodComponent,
availableGlobally: true,
availableForProject: false
availableForProject: false,
displayTab: true
},
{
key: ANALYSIS_SCOPE_CATEGORY,
name: translate('property.category.exclusions'),
renderComponent: getAnalysisScopeComponent,
availableGlobally: true,
availableForProject: true,
displayTab: false
}
];

function getLanguagesComponent(component: any, originalCategory: string) {
return <Languages component={component} selectedCategory={originalCategory} />;
function getLanguagesComponent(props: AdditionalCategoryComponentProps) {
return <Languages {...props} />;
}

function getNewCodePeriodComponent() {
return <NewCodePeriod />;
}

function getAnalysisScopeComponent(props: AdditionalCategoryComponentProps) {
return <AnalysisScope {...props} />;
}

+ 1
- 0
server/sonar-web/src/main/js/apps/settings/components/AdditionalCategoryKeys.ts View File

@@ -20,3 +20,4 @@

export const LANGUAGES_CATEGORY = 'languages';
export const NEW_CODE_PERIOD_CATEGORY = 'new_code_period';
export const ANALYSIS_SCOPE_CATEGORY = 'exclusions';

+ 7
- 5
server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.tsx View File

@@ -67,11 +67,13 @@ export class CategoriesList extends React.PureComponent<CategoriesListProps> {
name: getCategoryName(key)
}))
.concat(
this.props.component
? // Project settings
ADDITIONAL_CATEGORIES.filter(c => c.availableForProject)
: // Global settings
ADDITIONAL_CATEGORIES.filter(c => c.availableGlobally)
ADDITIONAL_CATEGORIES.filter(c => c.displayTab).filter(c =>
this.props.component
? // Project settings
c.availableForProject
: // Global settings
c.availableGlobally
)
);
const sortedCategories = sortBy(categoriesWithName, category => category.name.toLowerCase());
return (

+ 63
- 0
server/sonar-web/src/main/js/apps/settings/components/AnalysisScope.tsx View File

@@ -0,0 +1,63 @@
/*
* SonarQube
* Copyright (C) 2009-2019 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';
import { Link } from 'react-router';
import { translate } from 'sonar-ui-common/helpers/l10n';
import { AdditionalCategoryComponentProps } from './AdditionalCategories';
import CategoryDefinitionsList from './CategoryDefinitionsList';

export function AnalysisScope(props: AdditionalCategoryComponentProps) {
const { parentComponent, selectedCategory } = props;

return (
<>
<p className="spacer-bottom">
{translate('settings.analysis_scope.wildcards.introduction')}
<Link
className="spacer-left"
to="/documentation/project-administration/narrowing-the-focus/">
{translate('learn_more')}
</Link>
</p>

<table className="data spacer-bottom">
<tbody>
<tr>
<td>*</td>
<td>{translate('settings.analysis_scope.wildcards.zero_more_char')}</td>
</tr>
<tr>
<td>**</td>
<td>{translate('settings.analysis_scope.wildcards.zero_more_dir')}</td>
</tr>
<tr>
<td>?</td>
<td>{translate('settings.analysis_scope.wildcards.single_char')}</td>
</tr>
</tbody>
</table>

<div className="settings-sub-category">
<CategoryDefinitionsList category={selectedCategory} component={parentComponent} />
</div>
</>
);
}

+ 4
- 5
server/sonar-web/src/main/js/apps/settings/components/AppContainer.tsx View File

@@ -34,7 +34,6 @@ import AllCategoriesList from './AllCategoriesList';
import CategoryDefinitionsList from './CategoryDefinitionsList';
import { CATEGORY_OVERRIDES } from './CategoryOverrides';
import PageHeader from './PageHeader';
import WildcardsHelp from './WildcardsHelp';

interface Props {
component?: T.Component;
@@ -82,7 +81,6 @@ export class App extends React.PureComponent<Props & WithRouterProps, State> {
}

const { query } = this.props.location;

const originalCategory = (query.category as string) || this.props.defaultCategory;
const overriddenCategory = CATEGORY_OVERRIDES[originalCategory.toLowerCase()];
const selectedCategory = overriddenCategory || originalCategory;
@@ -110,15 +108,16 @@ export class App extends React.PureComponent<Props & WithRouterProps, State> {
</div>
<div className="side-tabs-main">
{foundAdditionalCategory && shouldRenderAdditionalCategory ? (
foundAdditionalCategory.renderComponent(this.props.component, originalCategory)
foundAdditionalCategory.renderComponent({
parentComponent: this.props.component,
selectedCategory: originalCategory
})
) : (
<CategoryDefinitionsList
category={selectedCategory}
component={this.props.component}
/>
)}

{selectedCategory === 'exclusions' && <WildcardsHelp />}
</div>
</div>
</div>

+ 0
- 122
server/sonar-web/src/main/js/apps/settings/components/WildcardsHelp.tsx View File

@@ -1,122 +0,0 @@
/*
* SonarQube
* Copyright (C) 2009-2019 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';
import { translate } from 'sonar-ui-common/helpers/l10n';

export default function WildcardsHelp() {
return (
<div className="huge-spacer-top">
<h2 className="spacer-bottom">{translate('settings.wildcards')}</h2>
<p className="spacer-bottom">{translate('settings.wildcards.following_rules_are_applied')}</p>

<table className="data spacer-bottom">
<tbody>
<tr>
<td>*</td>
<td>{translate('settings.wildcards.zero_more_char')}</td>
</tr>
<tr>
<td>**</td>
<td>{translate('settings.wildcards.zero_more_dir')}</td>
</tr>
<tr>
<td>?</td>
<td>{translate('settings.wildcards.single_char')}</td>
</tr>
</tbody>
</table>

<table className="data zebra">
<thead>
<tr>
<th>{translate('example')}</th>
<th>{translate('settings.wildcards.matches')}</th>
<th>{translate('settings.wildcards.does_no_match')}</th>
</tr>
</thead>
<tbody>
<tr>
<td>**/foo/*.js</td>
<td>
<ul>
<li>src/foo/bar.js</li>
<li>lib/ui/foo/bar.js</li>
</ul>
</td>
<td>
<ul>
<li>src/bar.js</li>
<li>src/foo2/bar.js</li>
</ul>
</td>
</tr>
<tr>
<td>src/foo/*bar*.js</td>
<td>
<ul>
<li>src/foo/bar.js</li>
<li>src/foo/bar1.js</li>
<li>src/foo/bar123.js</li>
<li>src/foo/123bar123.js</li>
</ul>
</td>
<td>
<ul>
<li>src/foo/ui/bar.js</li>
<li>src/bar.js</li>
</ul>
</td>
</tr>
<tr>
<td>src/foo/**</td>
<td>
<ul>
<li>src/foo/bar.js</li>
<li>src/foo/ui/bar.js</li>
</ul>
</td>
<td>
<ul>
<li>src/bar/foo/bar.js</li>
<li>src/bar.js</li>
</ul>
</td>
</tr>
<tr>
<td>**/foo?.js</td>
<td>
<ul>
<li>src/foo1.js</li>
<li>src/bar/foo1.js</li>
</ul>
</td>
<td>
<ul>
<li>src/foo.js</li>
<li>src/foo12.js</li>
<li>src/12foo3.js</li>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
);
}

+ 14
- 3
server/sonar-web/src/main/js/apps/settings/components/__tests__/AllCategoriesList-test.tsx View File

@@ -30,21 +30,32 @@ jest.mock('../AdditionalCategories', () => ({
name: 'CAT_1_NAME',
renderComponent: jest.fn(),
availableGlobally: true,
availableForProject: true
availableForProject: true,
displayTab: true
},
{
key: 'CAT_2',
name: 'CAT_2_NAME',
renderComponent: jest.fn(),
availableGlobally: true,
availableForProject: false
availableForProject: false,
displayTab: true
},
{
key: 'CAT_3',
name: 'CAT_3_NAME',
renderComponent: jest.fn(),
availableGlobally: false,
availableForProject: true
availableForProject: true,
displayTab: true
},
{
key: 'CAT_4',
name: 'CAT_4_NAME',
renderComponent: jest.fn(),
availableGlobally: true,
availableForProject: true,
displayTab: false
}
] as AdditionalCategory[]
}));

+ 32
- 0
server/sonar-web/src/main/js/apps/settings/components/__tests__/AnalysisScope-test.tsx View File

@@ -0,0 +1,32 @@
/*
* SonarQube
* Copyright (C) 2009-2019 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 { mockComponent } from '../../../../helpers/testMocks';
import { AnalysisScope } from '../AnalysisScope';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
});

function shallowRender() {
return shallow(<AnalysisScope parentComponent={mockComponent()} selectedCategory="TEST" />);
}

+ 24
- 4
server/sonar-web/src/main/js/apps/settings/components/__tests__/AppContainer-test.tsx View File

@@ -19,26 +19,46 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
import { mockLocation, mockRouter } from '../../../../helpers/testMocks';
import { LANGUAGES_CATEGORY, NEW_CODE_PERIOD_CATEGORY } from '../AdditionalCategoryKeys';
import {
ANALYSIS_SCOPE_CATEGORY,
LANGUAGES_CATEGORY,
NEW_CODE_PERIOD_CATEGORY
} from '../AdditionalCategoryKeys';
import { App } from '../AppContainer';

it('should render correctly', () => {
it('should render default view correctly', async () => {
const wrapper = shallowRender();

await waitAndUpdate(wrapper);
expect(wrapper).toMatchSnapshot();
});

it('should render newCodePeriod correctly', () => {
it('should render newCodePeriod correctly', async () => {
const wrapper = shallowRender({
location: mockLocation({ query: { category: NEW_CODE_PERIOD_CATEGORY } })
});

await waitAndUpdate(wrapper);
expect(wrapper).toMatchSnapshot();
});

it('should render languages correctly', () => {
it('should render languages correctly', async () => {
const wrapper = shallowRender({
location: mockLocation({ query: { category: LANGUAGES_CATEGORY } })
});

await waitAndUpdate(wrapper);
expect(wrapper).toMatchSnapshot();
});

it('should render analysis scope correctly', async () => {
const wrapper = shallowRender({
location: mockLocation({ query: { category: ANALYSIS_SCOPE_CATEGORY } })
});

await waitAndUpdate(wrapper);
expect(wrapper).toMatchSnapshot();
});


+ 79
- 0
server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AnalysisScope-test.tsx.snap View File

@@ -0,0 +1,79 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<Fragment>
<p
className="spacer-bottom"
>
settings.analysis_scope.wildcards.introduction
<Link
className="spacer-left"
onlyActiveOnIndex={false}
style={Object {}}
to="/documentation/project-administration/narrowing-the-focus/"
>
learn_more
</Link>
</p>
<table
className="data spacer-bottom"
>
<tbody>
<tr>
<td>
*
</td>
<td>
settings.analysis_scope.wildcards.zero_more_char
</td>
</tr>
<tr>
<td>
**
</td>
<td>
settings.analysis_scope.wildcards.zero_more_dir
</td>
</tr>
<tr>
<td>
?
</td>
<td>
settings.analysis_scope.wildcards.single_char
</td>
</tr>
</tbody>
</table>
<div
className="settings-sub-category"
>
<Connect(SubCategoryDefinitionsList)
category="TEST"
component={
Object {
"breadcrumbs": Array [],
"key": "my-project",
"name": "MyProject",
"organization": "foo",
"qualifier": "TRK",
"qualityGate": Object {
"isDefault": true,
"key": "30",
"name": "Sonar way",
},
"qualityProfiles": Array [
Object {
"deleted": false,
"key": "my-qp",
"language": "ts",
"name": "Sonar way",
},
],
"tags": Array [],
}
}
/>
</div>
</Fragment>
`;

+ 139
- 3
server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AppContainer-test.tsx.snap View File

@@ -1,7 +1,143 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `""`;
exports[`should render analysis scope correctly 1`] = `
<div
className="page page-limited"
id="settings-page"
>
<Suggestions
suggestions="settings"
/>
<HelmetWrapper
defer={true}
encodeSpecialCharacters={true}
title="settings.page"
/>
<PageHeader />
<div
className="side-tabs-layout settings-layout"
>
<div
className="side-tabs-side"
>
<Connect(CategoriesList)
defaultCategory="general"
selectedCategory="exclusions"
/>
</div>
<div
className="side-tabs-main"
>
<AnalysisScope
selectedCategory="exclusions"
/>
</div>
</div>
</div>
`;

exports[`should render languages correctly 1`] = `""`;
exports[`should render default view correctly 1`] = `
<div
className="page page-limited"
id="settings-page"
>
<Suggestions
suggestions="settings"
/>
<HelmetWrapper
defer={true}
encodeSpecialCharacters={true}
title="settings.page"
/>
<PageHeader />
<div
className="side-tabs-layout settings-layout"
>
<div
className="side-tabs-side"
>
<Connect(CategoriesList)
defaultCategory="general"
selectedCategory="general"
/>
</div>
<div
className="side-tabs-main"
>
<Connect(SubCategoryDefinitionsList)
category="general"
/>
</div>
</div>
</div>
`;

exports[`should render newCodePeriod correctly 1`] = `""`;
exports[`should render languages correctly 1`] = `
<div
className="page page-limited"
id="settings-page"
>
<Suggestions
suggestions="settings"
/>
<HelmetWrapper
defer={true}
encodeSpecialCharacters={true}
title="settings.page"
/>
<PageHeader />
<div
className="side-tabs-layout settings-layout"
>
<div
className="side-tabs-side"
>
<Connect(CategoriesList)
defaultCategory="general"
selectedCategory="languages"
/>
</div>
<div
className="side-tabs-main"
>
<withRouter(Connect(Languages))
selectedCategory="languages"
/>
</div>
</div>
</div>
`;

exports[`should render newCodePeriod correctly 1`] = `
<div
className="page page-limited"
id="settings-page"
>
<Suggestions
suggestions="settings"
/>
<HelmetWrapper
defer={true}
encodeSpecialCharacters={true}
title="settings.page"
/>
<PageHeader />
<div
className="side-tabs-layout settings-layout"
>
<div
className="side-tabs-side"
>
<Connect(CategoriesList)
defaultCategory="general"
selectedCategory="new_code_period"
/>
</div>
<div
className="side-tabs-main"
>
<NewCodePeriod />
</div>
</div>
</div>
`;

+ 5
- 7
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

@@ -907,13 +907,11 @@ settings.default.complex_value=<complex value>
settings.default.password=<password>
settings.reset_confirm.title=Reset Setting
settings.reset_confirm.description=Are you sure that you want to reset this setting?
settings.wildcards=Wildcards
settings.wildcards.following_rules_are_applied=Following rules are applied:
settings.wildcards.zero_more_char=Match zero or more characters
settings.wildcards.zero_more_dir=Match zero or more directories
settings.wildcards.single_char=Match a single character
settings.wildcards.matches=Matches
settings.wildcards.does_no_match=Does not match

settings.analysis_scope.wildcards.introduction=You can use the following wildcards.
settings.analysis_scope.wildcards.zero_more_char=Match zero or more characters
settings.analysis_scope.wildcards.zero_more_dir=Match zero or more directories
settings.analysis_scope.wildcards.single_char=Match a single character

settings.new_code_period.category=New Code Period
settings.new_code_period.title=Default New Code Period behavior

Loading…
Cancel
Save