diff options
author | Wouter Admiraal <wouter.admiraal@sonarsource.com> | 2020-02-04 11:16:04 +0100 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2020-02-20 20:46:17 +0100 |
commit | 3c110148c73be5775c03e8ce71ca7d426157d2ae (patch) | |
tree | 2d6d3fbe34c67874d81cc5e8fd1d0c0a66aea2d8 | |
parent | c1ea335081c7b03b9908f9b397e3993023ee3d62 (diff) | |
download | sonarqube-3c110148c73be5775c03e8ce71ca7d426157d2ae.tar.gz sonarqube-3c110148c73be5775c03e8ce71ca7d426157d2ae.zip |
SONAR-13035 Group global ALM related settings into a single category
92 files changed, 2407 insertions, 1549 deletions
diff --git a/server/sonar-auth-github/src/main/java/org/sonar/auth/github/GitHubSettings.java b/server/sonar-auth-github/src/main/java/org/sonar/auth/github/GitHubSettings.java index bd4871eb8a7..21b29139805 100644 --- a/server/sonar-auth-github/src/main/java/org/sonar/auth/github/GitHubSettings.java +++ b/server/sonar-auth-github/src/main/java/org/sonar/auth/github/GitHubSettings.java @@ -23,6 +23,7 @@ import java.util.Arrays; import java.util.List; import javax.annotation.CheckForNull; import javax.annotation.Nullable; +import org.sonar.api.CoreProperties; import org.sonar.api.config.Configuration; import org.sonar.api.config.PropertyDefinition; @@ -42,7 +43,7 @@ public class GitHubSettings { private static final String ORGANIZATIONS = "sonar.auth.github.organizations"; - private static final String CATEGORY = "security"; + private static final String CATEGORY = CoreProperties.CATEGORY_ALM_INTEGRATION; private static final String SUBCATEGORY = "github"; private final Configuration configuration; diff --git a/server/sonar-auth-gitlab/src/main/java/org/sonar/auth/gitlab/GitLabSettings.java b/server/sonar-auth-gitlab/src/main/java/org/sonar/auth/gitlab/GitLabSettings.java index 1cb130f662f..860d7e89fae 100644 --- a/server/sonar-auth-gitlab/src/main/java/org/sonar/auth/gitlab/GitLabSettings.java +++ b/server/sonar-auth-gitlab/src/main/java/org/sonar/auth/gitlab/GitLabSettings.java @@ -38,7 +38,7 @@ public class GitLabSettings { static final String GITLAB_AUTH_ALLOW_USERS_TO_SIGNUP = "sonar.auth.gitlab.allowUsersToSignUp"; static final String GITLAB_AUTH_SYNC_USER_GROUPS = "sonar.auth.gitlab.groupsSync"; - private static final String CATEGORY = CoreProperties.CATEGORY_SECURITY; + private static final String CATEGORY = CoreProperties.CATEGORY_ALM_INTEGRATION; private static final String SUBCATEGORY = "gitlab"; private final Configuration configuration; diff --git a/server/sonar-web/src/main/js/api/almSettings.ts b/server/sonar-web/src/main/js/api/alm-settings.ts index 6ac6b1c0963..6ac6b1c0963 100644 --- a/server/sonar-web/src/main/js/api/almSettings.ts +++ b/server/sonar-web/src/main/js/api/alm-settings.ts diff --git a/server/sonar-web/src/main/js/app/styles/init/misc.css b/server/sonar-web/src/main/js/app/styles/init/misc.css index c4e543c8f39..bdc61008d39 100644 --- a/server/sonar-web/src/main/js/app/styles/init/misc.css +++ b/server/sonar-web/src/main/js/app/styles/init/misc.css @@ -152,6 +152,14 @@ th.hide-overflow { padding-top: calc(var(--gridSize) / 2) !important; } +.big-padded-top { + padding-top: calc(2 * var(--gridSize)); +} + +.huge-padded-top { + padding-top: 40px; +} + td.little-spacer-left { padding-left: 4px !important; } @@ -305,6 +313,10 @@ th.huge-spacer-right { width: 600px !important; } +.abs-height-100 { + height: 100% !important; +} + .max-height-100 { max-height: 100% !important; } diff --git a/server/sonar-web/src/main/js/apps/create/project/CreateProjectPageSonarQube.tsx b/server/sonar-web/src/main/js/apps/create/project/CreateProjectPageSonarQube.tsx index d0a8ced9353..d9a7c160070 100644 --- a/server/sonar-web/src/main/js/apps/create/project/CreateProjectPageSonarQube.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/CreateProjectPageSonarQube.tsx @@ -22,11 +22,11 @@ import { Helmet } from 'react-helmet-async'; import { WithRouterProps } from 'react-router'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { addWhitePageClass, removeWhitePageClass } from 'sonar-ui-common/helpers/pages'; -import { getAlmSettings } from '../../../api/almSettings'; +import { getAlmSettings } from '../../../api/alm-settings'; import { whenLoggedIn } from '../../../components/hoc/whenLoggedIn'; import { withAppState } from '../../../components/hoc/withAppState'; import { getProjectUrl } from '../../../helpers/urls'; -import { AlmSettingsInstance, ALM_KEYS } from '../../../types/alm-settings'; +import { AlmKeys, AlmSettingsInstance } from '../../../types/alm-settings'; import BitbucketProjectCreate from './BitbucketProjectCreate'; import CreateProjectModeSelection from './CreateProjectModeSelection'; import ManualProjectCreate from './ManualProjectCreate'; @@ -81,7 +81,7 @@ export class CreateProjectPageSonarQube extends React.PureComponent<Props, State .then(almSettings => { if (this.mounted) { this.setState({ - bitbucketSettings: almSettings.filter(s => s.alm === ALM_KEYS.BITBUCKET), + bitbucketSettings: almSettings.filter(s => s.alm === AlmKeys.Bitbucket), loading: false }); } diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketPersonalAccessTokenForm-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketPersonalAccessTokenForm-test.tsx index 316871fe7c5..5dc8bcb0b67 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketPersonalAccessTokenForm-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketPersonalAccessTokenForm-test.tsx @@ -23,7 +23,7 @@ import * as React from 'react'; import { SubmitButton } from 'sonar-ui-common/components/controls/buttons'; import { change, submit, waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; import { mockAlmSettingsInstance } from '../../../../helpers/mocks/alm-settings'; -import { ALM_KEYS } from '../../../../types/alm-settings'; +import { AlmKeys } from '../../../../types/alm-settings'; import BitbucketPersonalAccessTokenForm, { BitbucketPersonalAccessTokenFormProps } from '../BitbucketPersonalAccessTokenForm'; @@ -54,7 +54,7 @@ function shallowRender(props: Partial<BitbucketPersonalAccessTokenFormProps> = { return shallow<BitbucketPersonalAccessTokenFormProps>( <BitbucketPersonalAccessTokenForm bitbucketSetting={mockAlmSettingsInstance({ - alm: ALM_KEYS.BITBUCKET, + alm: AlmKeys.Bitbucket, url: 'http://www.example.com' })} onPersonalAccessTokenCreate={jest.fn()} diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketProjectCreate-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketProjectCreate-test.tsx index 91fb4650308..3d9829f03d5 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketProjectCreate-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketProjectCreate-test.tsx @@ -31,7 +31,7 @@ import { import { mockBitbucketRepository } from '../../../../helpers/mocks/alm-integrations'; import { mockAlmSettingsInstance } from '../../../../helpers/mocks/alm-settings'; import { mockLocation } from '../../../../helpers/testMocks'; -import { ALM_KEYS } from '../../../../types/alm-settings'; +import { AlmKeys } from '../../../../types/alm-settings'; import { BitbucketProjectCreate } from '../BitbucketProjectCreate'; jest.mock('../../../../api/alm-integrations', () => { @@ -129,7 +129,7 @@ it('should correctly import a repo', async () => { function shallowRender(props: Partial<BitbucketProjectCreate['props']> = {}) { return shallow<BitbucketProjectCreate>( <BitbucketProjectCreate - bitbucketSettings={[mockAlmSettingsInstance({ alm: ALM_KEYS.BITBUCKET, key: 'foo' })]} + bitbucketSettings={[mockAlmSettingsInstance({ alm: AlmKeys.Bitbucket, key: 'foo' })]} loadingBindings={false} location={mockLocation()} onProjectCreate={jest.fn()} diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketProjectCreateRenderer-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketProjectCreateRenderer-test.tsx index 66e237bc92b..e1b5b8537df 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketProjectCreateRenderer-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/BitbucketProjectCreateRenderer-test.tsx @@ -25,7 +25,7 @@ import { mockBitbucketRepository } from '../../../../helpers/mocks/alm-integrations'; import { mockAlmSettingsInstance } from '../../../../helpers/mocks/alm-settings'; -import { ALM_KEYS } from '../../../../types/alm-settings'; +import { AlmKeys } from '../../../../types/alm-settings'; import BitbucketProjectCreateRenderer, { BitbucketProjectCreateRendererProps } from '../BitbucketProjectCreateRenderer'; @@ -49,7 +49,7 @@ it('should render correctly', () => { function shallowRender(props: Partial<BitbucketProjectCreateRendererProps> = {}) { return shallow<BitbucketProjectCreateRendererProps>( <BitbucketProjectCreateRenderer - bitbucketSetting={mockAlmSettingsInstance({ alm: ALM_KEYS.BITBUCKET })} + bitbucketSetting={mockAlmSettingsInstance({ alm: AlmKeys.Bitbucket })} importing={false} loading={false} onImportRepository={jest.fn()} diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectPageSonarQube-test.tsx b/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectPageSonarQube-test.tsx index dbc4aeff690..e328df5b71a 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectPageSonarQube-test.tsx +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/CreateProjectPageSonarQube-test.tsx @@ -21,14 +21,14 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { addWhitePageClass } from 'sonar-ui-common/helpers/pages'; -import { getAlmSettings } from '../../../../api/almSettings'; +import { getAlmSettings } from '../../../../api/alm-settings'; import { mockLocation, mockLoggedInUser, mockRouter } from '../../../../helpers/testMocks'; -import { ALM_KEYS } from '../../../../types/alm-settings'; +import { AlmKeys } from '../../../../types/alm-settings'; import { CreateProjectPageSonarQube } from '../CreateProjectPageSonarQube'; import { CreateProjectModes } from '../types'; -jest.mock('../../../../api/almSettings', () => ({ - getAlmSettings: jest.fn().mockResolvedValue([{ alm: ALM_KEYS.BITBUCKET, key: 'foo' }]) +jest.mock('../../../../api/alm-settings', () => ({ + getAlmSettings: jest.fn().mockResolvedValue([{ alm: AlmKeys.Bitbucket, key: 'foo' }]) })); jest.mock('sonar-ui-common/helpers/pages', () => ({ diff --git a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/BitbucketProjectCreateRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/BitbucketProjectCreateRenderer-test.tsx.snap index f887af050b0..fa62e434607 100644 --- a/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/BitbucketProjectCreateRenderer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/create/project/__tests__/__snapshots__/BitbucketProjectCreateRenderer-test.tsx.snap @@ -179,7 +179,7 @@ exports[`should render correctly: invalid config, admin user 1`] = ` Object { "pathname": "/admin/settings", "query": Object { - "category": "pull_request_decoration", + "category": "almintegration", }, } } diff --git a/server/sonar-web/src/main/js/apps/settings/components/AdditionalCategories.tsx b/server/sonar-web/src/main/js/apps/settings/components/AdditionalCategories.tsx index ced4af67ee2..ff970731023 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/AdditionalCategories.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/AdditionalCategories.tsx @@ -20,16 +20,16 @@ import * as React from 'react'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { + ALM_INTEGRATION, ANALYSIS_SCOPE_CATEGORY, LANGUAGES_CATEGORY, NEW_CODE_PERIOD_CATEGORY, - PULL_REQUEST_DECORATION_BINDING_CATEGORY, - PULL_REQUEST_DECORATION_CATEGORY + PULL_REQUEST_DECORATION_BINDING_CATEGORY } from './AdditionalCategoryKeys'; +import AlmIntegration from './almIntegration/AlmIntegration'; import { AnalysisScope } from './AnalysisScope'; import Languages from './Languages'; import NewCodePeriod from './NewCodePeriod'; -import PullRequestDecoration from './pullRequestDecoration/PullRequestDecoration'; import PullRequestDecorationBinding from './pullRequestDecorationBinding/PRDecorationBinding'; export interface AdditionalCategoryComponentProps { @@ -73,13 +73,12 @@ export const ADDITIONAL_CATEGORIES: AdditionalCategory[] = [ displayTab: false }, { - key: PULL_REQUEST_DECORATION_CATEGORY, - name: translate('settings.pr_decoration.category'), - renderComponent: getPullRequestDecorationComponent, + key: ALM_INTEGRATION, + name: translate('property.category.almintegration'), + renderComponent: getAlmIntegrationComponent, availableGlobally: true, availableForProject: false, - displayTab: true, - requiresBranchesEnabled: true + displayTab: false }, { key: PULL_REQUEST_DECORATION_BINDING_CATEGORY, @@ -104,8 +103,8 @@ function getAnalysisScopeComponent(props: AdditionalCategoryComponentProps) { return <AnalysisScope {...props} />; } -function getPullRequestDecorationComponent() { - return <PullRequestDecoration />; +function getAlmIntegrationComponent(props: AdditionalCategoryComponentProps) { + return <AlmIntegration {...props} />; } function getPullRequestDecorationBindingComponent(props: AdditionalCategoryComponentProps) { diff --git a/server/sonar-web/src/main/js/apps/settings/components/AdditionalCategoryKeys.ts b/server/sonar-web/src/main/js/apps/settings/components/AdditionalCategoryKeys.ts index b4381b96ade..bea73ef2354 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/AdditionalCategoryKeys.ts +++ b/server/sonar-web/src/main/js/apps/settings/components/AdditionalCategoryKeys.ts @@ -17,8 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +export const ALM_INTEGRATION = 'almintegration'; export const ANALYSIS_SCOPE_CATEGORY = 'exclusions'; export const LANGUAGES_CATEGORY = 'languages'; export const NEW_CODE_PERIOD_CATEGORY = 'new_code_period'; -export const PULL_REQUEST_DECORATION_CATEGORY = 'pull_request_decoration'; export const PULL_REQUEST_DECORATION_BINDING_CATEGORY = 'pull_request_decoration_binding'; diff --git a/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx b/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx index f3876806997..49bf0ab7478 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.tsx @@ -28,6 +28,7 @@ interface Props { component?: T.Component; fetchValues: Function; settings: Array<T.Setting & { definition: T.SettingCategoryDefinition }>; + subCategory?: string; } export default class SubCategoryDefinitionsList extends React.PureComponent<Props> { @@ -66,9 +67,12 @@ export default class SubCategoryDefinitionsList extends React.PureComponent<Prop const sortedSubCategories = sortBy(subCategories, subCategory => subCategory.name.toLowerCase() ); + const filteredSubCategories = this.props.subCategory + ? sortedSubCategories.filter(c => c.key === this.props.subCategory) + : sortedSubCategories; return ( <ul className="settings-sub-categories-list"> - {sortedSubCategories.map(subCategory => ( + {filteredSubCategories.map(subCategory => ( <li key={subCategory.key}> <h2 className="settings-sub-category-name">{subCategory.name}</h2> {subCategory.description != null && ( diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/AppContainer-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/__tests__/AppContainer-test.tsx index 34a2065611b..eb9f80b6741 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/AppContainer-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/AppContainer-test.tsx @@ -22,11 +22,11 @@ import * as React from 'react'; import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; import { mockLocation, mockRouter } from '../../../../helpers/testMocks'; import { + ALM_INTEGRATION, ANALYSIS_SCOPE_CATEGORY, LANGUAGES_CATEGORY, NEW_CODE_PERIOD_CATEGORY, - PULL_REQUEST_DECORATION_BINDING_CATEGORY, - PULL_REQUEST_DECORATION_CATEGORY + PULL_REQUEST_DECORATION_BINDING_CATEGORY } from '../AdditionalCategoryKeys'; import { App } from '../AppContainer'; @@ -64,9 +64,9 @@ it('should render analysis scope correctly', async () => { expect(wrapper).toMatchSnapshot(); }); -it('should render pull request decoration correctly', async () => { +it('should render ALM integration correctly', async () => { const wrapper = shallowRender({ - location: mockLocation({ query: { category: PULL_REQUEST_DECORATION_CATEGORY } }) + location: mockLocation({ query: { category: ALM_INTEGRATION } }) }); await waitAndUpdate(wrapper); diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AdditionalCategories-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AdditionalCategories-test.tsx.snap index 2a421ce756f..803fcbfe625 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AdditionalCategories-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AdditionalCategories-test.tsx.snap @@ -60,7 +60,34 @@ exports[`should render additional categories component correctly 3`] = ` /> `; -exports[`should render additional categories component correctly 4`] = `<PullRequestDecoration />`; +exports[`should render additional categories component correctly 4`] = ` +<Connect(withAppState(AlmIntegration)) + 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 [], + } + } + selectedCategory="TEST" +/> +`; exports[`should render additional categories component correctly 5`] = ` <PRDecorationBinding diff --git a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AppContainer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AppContainer-test.tsx.snap index cabe4ab6945..a650f8e78fa 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AppContainer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/__tests__/__snapshots__/AppContainer-test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should render analysis scope correctly 1`] = ` +exports[`should render ALM integration correctly 1`] = ` <div className="page page-limited" id="settings-page" @@ -22,21 +22,21 @@ exports[`should render analysis scope correctly 1`] = ` > <Connect(CategoriesList) defaultCategory="general" - selectedCategory="exclusions" + selectedCategory="almintegration" /> </div> <div className="side-tabs-main" > - <AnalysisScope - selectedCategory="exclusions" + <Connect(withAppState(AlmIntegration)) + selectedCategory="almintegration" /> </div> </div> </div> `; -exports[`should render default view correctly 1`] = ` +exports[`should render analysis scope correctly 1`] = ` <div className="page page-limited" id="settings-page" @@ -58,21 +58,21 @@ exports[`should render default view correctly 1`] = ` > <Connect(CategoriesList) defaultCategory="general" - selectedCategory="general" + selectedCategory="exclusions" /> </div> <div className="side-tabs-main" > - <Connect(SubCategoryDefinitionsList) - category="general" + <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" @@ -94,21 +94,21 @@ exports[`should render languages correctly 1`] = ` > <Connect(CategoriesList) defaultCategory="general" - selectedCategory="languages" + selectedCategory="general" /> </div> <div className="side-tabs-main" > - <withRouter(Connect(Languages)) - selectedCategory="languages" + <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" @@ -130,19 +130,21 @@ exports[`should render newCodePeriod correctly 1`] = ` > <Connect(CategoriesList) defaultCategory="general" - selectedCategory="new_code_period" + selectedCategory="languages" /> </div> <div className="side-tabs-main" > - <NewCodePeriod /> + <withRouter(Connect(Languages)) + selectedCategory="languages" + /> </div> </div> </div> `; -exports[`should render pull request decoration binding correctly 1`] = ` +exports[`should render newCodePeriod correctly 1`] = ` <div className="page page-limited" id="settings-page" @@ -164,21 +166,19 @@ exports[`should render pull request decoration binding correctly 1`] = ` > <Connect(CategoriesList) defaultCategory="general" - selectedCategory="pull_request_decoration_binding" + selectedCategory="new_code_period" /> </div> <div className="side-tabs-main" > - <Connect(SubCategoryDefinitionsList) - category="pull_request_decoration_binding" - /> + <NewCodePeriod /> </div> </div> </div> `; -exports[`should render pull request decoration correctly 1`] = ` +exports[`should render pull request decoration binding correctly 1`] = ` <div className="page page-limited" id="settings-page" @@ -200,13 +200,15 @@ exports[`should render pull request decoration correctly 1`] = ` > <Connect(CategoriesList) defaultCategory="general" - selectedCategory="pull_request_decoration" + selectedCategory="pull_request_decoration_binding" /> </div> <div className="side-tabs-main" > - <PullRequestDecoration /> + <Connect(SubCategoryDefinitionsList) + category="pull_request_decoration_binding" + /> </div> </div> </div> diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmPRDecorationForm.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionForm.tsx index 705a1083ad9..1f4de028fe2 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmPRDecorationForm.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionForm.tsx @@ -19,11 +19,11 @@ */ import { isEqual, omit } from 'lodash'; import * as React from 'react'; -import { AlmSettingsBinding, ALM_KEYS } from '../../../../types/alm-settings'; -import AlmPRDecorationFormModalRenderer from './AlmPRDecorationFormModalRenderer'; -import AlmPRDecorationFormRenderer from './AlmPRDecorationFormRenderer'; +import { AlmBindingDefinition } from '../../../../types/alm-settings'; +import AlmBindingDefinitionFormModalRenderer from './AlmBindingDefinitionFormModalRenderer'; +import AlmBindingDefinitionFormRenderer from './AlmBindingDefinitionFormRenderer'; -export interface AlmPRDecorationFormChildrenProps<B> { +export interface AlmBindingDefinitionFormChildrenProps<B> { formData: B; hideKeyField?: boolean; onFieldChange: (fieldId: keyof B, value: string) => void; @@ -31,9 +31,8 @@ export interface AlmPRDecorationFormChildrenProps<B> { } interface Props<B> { - alm: ALM_KEYS; bindingDefinition: B; - children: (props: AlmPRDecorationFormChildrenProps<B>) => React.ReactNode; + children: (props: AlmBindingDefinitionFormChildrenProps<B>) => React.ReactNode; help?: React.ReactNode; hideKeyField?: boolean; loading?: boolean; @@ -51,10 +50,9 @@ interface State<B> { touched: boolean; } -export default class AlmPRDecorationForm<B extends AlmSettingsBinding> extends React.PureComponent< - Props<B>, - State<B> -> { +export default class AlmBindingDefinitionForm< + B extends AlmBindingDefinition +> extends React.PureComponent<Props<B>, State<B>> { constructor(props: Props<B>) { super(props); this.state = { formData: props.bindingDefinition, touched: false }; @@ -115,7 +113,6 @@ export default class AlmPRDecorationForm<B extends AlmSettingsBinding> extends R render() { const { - alm, bindingDefinition, children, help, @@ -132,9 +129,8 @@ export default class AlmPRDecorationForm<B extends AlmSettingsBinding> extends R const showDelete = showEdit && this.props.onDelete !== undefined; return showInModal ? ( - <AlmPRDecorationFormModalRenderer + <AlmBindingDefinitionFormModalRenderer action={bindingDefinition.key ? 'edit' : 'create'} - alm={alm} canSubmit={this.canSubmit} help={help} onCancel={this.handleCancel} @@ -143,9 +139,9 @@ export default class AlmPRDecorationForm<B extends AlmSettingsBinding> extends R formData, onFieldChange: this.handleFieldChange })} - </AlmPRDecorationFormModalRenderer> + </AlmBindingDefinitionFormModalRenderer> ) : ( - <AlmPRDecorationFormRenderer + <AlmBindingDefinitionFormRenderer canSubmit={this.canSubmit} help={help} loading={loading} @@ -160,7 +156,7 @@ export default class AlmPRDecorationForm<B extends AlmSettingsBinding> extends R onFieldChange: this.handleFieldChange, readOnly })} - </AlmPRDecorationFormRenderer> + </AlmBindingDefinitionFormRenderer> ); } } diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmDefinitionFormField.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionFormField.tsx index d9733f60808..8cd42e1221f 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmDefinitionFormField.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionFormField.tsx @@ -20,9 +20,9 @@ import * as React from 'react'; import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { AlmSettingsBinding } from '../../../../types/alm-settings'; +import { AlmBindingDefinition } from '../../../../types/alm-settings'; -export interface AlmDefinitionFormFieldProps<B extends AlmSettingsBinding> { +export interface AlmBindingDefinitionFormFieldProps<B extends AlmBindingDefinition> { autoFocus?: boolean; help?: React.ReactNode; id: string; @@ -34,8 +34,8 @@ export interface AlmDefinitionFormFieldProps<B extends AlmSettingsBinding> { value: string; } -export function AlmDefinitionFormField<B extends AlmSettingsBinding>( - props: AlmDefinitionFormFieldProps<B> +export function AlmBindingDefinitionFormField<B extends AlmBindingDefinition>( + props: AlmBindingDefinitionFormFieldProps<B> ) { const { autoFocus, @@ -52,7 +52,7 @@ export function AlmDefinitionFormField<B extends AlmSettingsBinding>( return ( <div className="modal-field"> <label className="display-flex-center" htmlFor={id}> - {translate('settings.pr_decoration.form', id)} + {translate('settings.almintegration.form', id)} <em className="mandatory spacer-right">*</em> {help && <HelpTooltip overlay={help} placement="right" />} </label> diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmPRDecorationFormModalRenderer.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionFormModalRenderer.tsx index 082aa0985d3..75bb9c0d378 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmPRDecorationFormModalRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionFormModalRenderer.tsx @@ -23,11 +23,9 @@ import SimpleModal from 'sonar-ui-common/components/controls/SimpleModal'; import { Alert } from 'sonar-ui-common/components/ui/Alert'; import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { ALM_KEYS } from '../../../../types/alm-settings'; -export interface AlmPRDecorationFormModalProps { +export interface AlmBindingDefinitionFormModalProps { action: 'edit' | 'create'; - alm: ALM_KEYS; canSubmit: () => boolean; children: React.ReactNode; help?: React.ReactNode; @@ -35,14 +33,11 @@ export interface AlmPRDecorationFormModalProps { onSubmit: () => void; } -export default function AlmPRDecorationFormModalRenderer(props: AlmPRDecorationFormModalProps) { - const { alm, action, children, help } = props; - const header = translate( - 'settings', - alm === ALM_KEYS.GITLAB ? 'mr_decoration' : 'pr_decoration', - 'form.header', - action - ); +export default function AlmBindingDefinitionFormModalRenderer( + props: AlmBindingDefinitionFormModalProps +) { + const { action, children, help } = props; + const header = translate('settings.almintegration.form.header', action); return ( <SimpleModal header={header} onClose={props.onCancel} onSubmit={props.onSubmit} size="medium"> @@ -53,18 +48,21 @@ export default function AlmPRDecorationFormModalRenderer(props: AlmPRDecorationF </div> <div className="modal-body modal-container"> - {help && ( - <Alert className="big-spacer-bottom" variant="info"> - {help} - </Alert> - )} - {children} + <div className="display-flex-start"> + <div className="flex-1">{children}</div> + + {help && ( + <Alert className="huge-spacer-left flex-1" variant="info"> + {help} + </Alert> + )} + </div> </div> <div className="modal-foot"> <DeferredSpinner className="spacer-right" loading={submitting} /> <SubmitButton disabled={submitting || !props.canSubmit()}> - {translate('settings.pr_decoration.form.save')} + {translate('settings.almintegration.form.save')} </SubmitButton> <ResetButtonLink onClick={onCloseClick}>{translate('cancel')}</ResetButtonLink> </div> diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmPRDecorationFormRenderer.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionFormRenderer.tsx index e21a1da2872..812ba48a61a 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmPRDecorationFormRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionFormRenderer.tsx @@ -24,7 +24,7 @@ import { Alert } from 'sonar-ui-common/components/ui/Alert'; import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; import { translate } from 'sonar-ui-common/helpers/l10n'; -export interface AlmPRDecorationFormRendererProps { +export interface AlmBindingDefinitionFormRendererProps { canSubmit: () => boolean; children: React.ReactNode; help?: React.ReactNode; @@ -36,7 +36,9 @@ export interface AlmPRDecorationFormRendererProps { success: boolean; } -export default function AlmPRDecorationFormRenderer(props: AlmPRDecorationFormRendererProps) { +export default function AlmBindingDefinitionFormRenderer( + props: AlmBindingDefinitionFormRendererProps +) { const { children, help, loading, success } = props; return ( @@ -47,40 +49,47 @@ export default function AlmPRDecorationFormRenderer(props: AlmPRDecorationFormRe e.preventDefault(); props.onSubmit(); }}> - {help && ( - <Alert className="big-spacer-bottom" variant="info"> - {help} - </Alert> - )} + <div className="display-flex-start"> + <div className="flex-1"> + {children} - {children} + <div className="display-flex-center"> + {props.onEdit === undefined ? ( + <SubmitButton disabled={loading || !props.canSubmit()}> + {translate('settings.almintegration.form.save')} + </SubmitButton> + ) : ( + <Button disabled={loading} onClick={props.onEdit}> + {translate('edit')} + </Button> + )} + {props.onDelete && ( + <Button + className="button-red spacer-left" + disabled={loading} + onClick={props.onDelete}> + {translate('delete')} + </Button> + )} + {props.onCancel && ( + <ResetButtonLink className="spacer-left" onClick={props.onCancel}> + {translate('cancel')} + </ResetButtonLink> + )} + {loading && <DeferredSpinner className="spacer-left" />} + {!loading && success && ( + <span className="text-success spacer-left"> + <AlertSuccessIcon className="spacer-right" /> + {translate('settings.state.saved')} + </span> + )} + </div> + </div> - <div className="display-flex-center"> - {props.onEdit === undefined ? ( - <SubmitButton disabled={loading || !props.canSubmit()}> - {translate('settings.pr_decoration.form.save')} - </SubmitButton> - ) : ( - <Button disabled={loading} onClick={props.onEdit}> - {translate('edit')} - </Button> - )} - {props.onDelete && ( - <Button className="button-red spacer-left" disabled={loading} onClick={props.onDelete}> - {translate('delete')} - </Button> - )} - {props.onCancel && ( - <ResetButtonLink className="spacer-left" onClick={props.onCancel}> - {translate('cancel')} - </ResetButtonLink> - )} - {loading && <DeferredSpinner className="spacer-left" />} - {!loading && success && ( - <span className="text-success spacer-left"> - <AlertSuccessIcon className="spacer-right" /> - {translate('settings.state.saved')} - </span> + {help && ( + <Alert className="huge-spacer-left flex-1" variant="info"> + {help} + </Alert> )} </div> </form> diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmPRDecorationTable.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionsTable.tsx index 4ef416d5ee5..65253c4d6e0 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmPRDecorationTable.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmBindingDefinitionsTable.tsx @@ -21,11 +21,12 @@ import * as React from 'react'; import { Button, ButtonIcon, DeleteButton } from 'sonar-ui-common/components/controls/buttons'; import EditIcon from 'sonar-ui-common/components/icons/EditIcon'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { ALM_KEYS } from '../../../../types/alm-settings'; +import { AlmKeys } from '../../../../types/alm-settings'; -export interface AlmPRDecorationTableProps { +export interface AlmBindingDefinitionsTableProps { additionalColumnsHeaders: Array<string>; - alm: ALM_KEYS; + additionalTableInfo?: React.ReactNode; + alm: AlmKeys; definitions: Array<{ key: string; additionalColumns: Array<string>; @@ -35,36 +36,34 @@ export interface AlmPRDecorationTableProps { onEdit: (definitionKey: string) => void; } -export default function AlmPRDecorationTable(props: AlmPRDecorationTableProps) { - const { additionalColumnsHeaders, alm, definitions } = props; +export default function AlmBindingDefinitionsTable(props: AlmBindingDefinitionsTableProps) { + const { additionalColumnsHeaders, additionalTableInfo, alm, definitions } = props; return ( <> <div className="spacer-top big-spacer-bottom display-flex-space-between"> - <h4 className="display-inline"> - {translate( - 'settings', - alm === ALM_KEYS.GITLAB ? 'mr_decoration' : 'pr_decoration', - 'table.title' - )} - </h4> + <h2 className="settings-sub-category-name"> + {translate('settings.almintegration.table.title')} + </h2> <Button data-test="settings__alm-create" onClick={props.onCreate}> - {translate('settings.pr_decoration.table.create')} + {translate('settings.almintegration.table.create')} </Button> </div> + {additionalTableInfo} + <table className="data zebra fixed spacer-bottom"> <thead> <tr> - <th>{translate('settings.pr_decoration.table.column.name')}</th> + <th>{translate('settings.almintegration.table.column.name')}</th> {additionalColumnsHeaders.map(h => ( <th key={h}>{h}</th> ))} <th className="action-small text-center"> - {translate('settings.pr_decoration.table.column.edit')} + {translate('settings.almintegration.table.column.edit')} </th> <th className="action text-center"> - {translate('settings.pr_decoration.table.column.delete')} + {translate('settings.almintegration.table.column.delete')} </th> </tr> </thead> @@ -72,7 +71,7 @@ export default function AlmPRDecorationTable(props: AlmPRDecorationTableProps) { {definitions.length === 0 ? ( <tr data-test="settings__alm-empty-table"> <td colSpan={3 + additionalColumnsHeaders.length}> - {translate('settings.pr_decoration.table.empty', alm)} + {translate('settings.almintegration.table.empty', alm)} </td> </tr> ) : ( diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/PullRequestDecoration.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegration.tsx index 21484d5c03f..ee060e8f2bf 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/PullRequestDecoration.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegration.tsx @@ -22,27 +22,33 @@ import { countBindedProjects, deleteConfiguration, getAlmDefinitions -} from '../../../../api/almSettings'; -import { AlmSettingsBindingDefinitions, ALM_KEYS } from '../../../../types/alm-settings'; -import PRDecorationTabs from './PRDecorationTabs'; +} from '../../../../api/alm-settings'; +import { withAppState } from '../../../../components/hoc/withAppState'; +import { AlmKeys, AlmSettingsBindingDefinitions } from '../../../../types/alm-settings'; +import AlmIntegrationRenderer from './AlmIntegrationRenderer'; + +interface Props { + appState: Pick<T.AppState, 'branchesEnabled' | 'multipleAlmEnabled'>; + component?: T.Component; +} interface State { - currentAlm: ALM_KEYS; + currentAlm: AlmKeys; definitionKeyForDeletion?: string; definitions: AlmSettingsBindingDefinitions; loading: boolean; projectCount?: number; } -export default class PullRequestDecoration extends React.PureComponent<{}, State> { +export class AlmIntegration extends React.PureComponent<Props, State> { mounted = false; state: State = { - currentAlm: ALM_KEYS.GITHUB, + currentAlm: AlmKeys.GitHub, definitions: { - [ALM_KEYS.AZURE]: [], - [ALM_KEYS.BITBUCKET]: [], - [ALM_KEYS.GITHUB]: [], - [ALM_KEYS.GITLAB]: [] + [AlmKeys.Azure]: [], + [AlmKeys.Bitbucket]: [], + [AlmKeys.GitHub]: [], + [AlmKeys.GitLab]: [] }, loading: true }; @@ -67,6 +73,14 @@ export default class PullRequestDecoration extends React.PureComponent<{}, State }; fetchPullRequestDecorationSetting = () => { + const { + appState: { branchesEnabled } + } = this.props; + + if (!branchesEnabled) { + return Promise.resolve(); + } + this.setState({ loading: true }); return getAlmDefinitions() .then(definitions => { @@ -84,7 +98,7 @@ export default class PullRequestDecoration extends React.PureComponent<{}, State }); }; - handleSelectAlm = (currentAlm: ALM_KEYS) => { + handleSelectAlm = (currentAlm: AlmKeys) => { this.setState({ currentAlm }); }; @@ -112,8 +126,15 @@ export default class PullRequestDecoration extends React.PureComponent<{}, State }; render() { + const { + appState: { branchesEnabled, multipleAlmEnabled }, + component + } = this.props; return ( - <PRDecorationTabs + <AlmIntegrationRenderer + branchesEnabled={Boolean(branchesEnabled)} + component={component} + multipleAlmEnabled={Boolean(multipleAlmEnabled)} onCancel={this.handleCancel} onConfirmDelete={this.deleteConfiguration} onDelete={this.handleDelete} @@ -124,3 +145,5 @@ export default class PullRequestDecoration extends React.PureComponent<{}, State ); } } + +export default withAppState(AlmIntegration); diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegrationFeatureBox.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegrationFeatureBox.tsx new file mode 100644 index 00000000000..dfdeb10bbd5 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegrationFeatureBox.tsx @@ -0,0 +1,67 @@ +/* + * 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 classNames from 'classnames'; +import * as React from 'react'; +import CheckIcon from 'sonar-ui-common/components/icons/CheckIcon'; +import ClearIcon from 'sonar-ui-common/components/icons/ClearIcon'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import { colors } from '../../../../app/theme'; + +export interface AlmIntegrationFeatureBoxProps { + active: boolean; + description: React.ReactNode; + inactiveReason?: React.ReactNode; + name: React.ReactNode; +} + +export default function AlmIntegrationFeatureBox(props: AlmIntegrationFeatureBoxProps) { + const { active, description, inactiveReason, name } = props; + + return ( + <div + className={classNames( + 'boxed-group-inner display-flex-start width-30 spacer-right spacer-bottom bordered', + { + 'bg-muted': !active + } + )}> + {active ? ( + <CheckIcon className="little-spacer-top spacer-right" fill={colors.green} /> + ) : ( + <ClearIcon className="little-spacer-top spacer-right" fill={colors.gray60} /> + )} + <div className="display-flex-column abs-height-100"> + <h4>{name}</h4> + + <div className="spacer-top flex-1">{description}</div> + + <div className="spacer-top"> + {active ? ( + <em className="text-success">{translate('settings.almintegration.feature.enabled')}</em> + ) : ( + <em className="text-muted"> + {inactiveReason || translate('settings.almintegration.feature.disabled')} + </em> + )} + </div> + </div> + </div> + ); +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegrationRenderer.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegrationRenderer.tsx new file mode 100644 index 00000000000..c5c64310a4f --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmIntegrationRenderer.tsx @@ -0,0 +1,185 @@ +/* + * 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'; +import BoxedTabs from 'sonar-ui-common/components/controls/BoxedTabs'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import { getBaseUrl } from 'sonar-ui-common/helpers/urls'; +import { AlmKeys, AlmSettingsBindingDefinitions } from '../../../../types/alm-settings'; +import AzureTab from './AzureTab'; +import BitbucketTab from './BitbucketTab'; +import DeleteModal from './DeleteModal'; +import GithubTab from './GithubTab'; +import GitlabTab from './GitlabTab'; + +export interface AlmIntegrationRendererProps { + branchesEnabled: boolean; + component?: T.Component; + currentAlm: AlmKeys; + definitionKeyForDeletion?: string; + definitions: AlmSettingsBindingDefinitions; + loading: boolean; + multipleAlmEnabled: boolean; + onCancel: () => void; + onConfirmDelete: (definitionKey: string) => void; + onDelete: (definitionKey: string) => void; + onSelectAlm: (alm: AlmKeys) => void; + onUpdateDefinitions: () => void; + projectCount?: number; +} + +const tabs = [ + { + key: AlmKeys.GitHub, + label: ( + <> + <img + alt="github" + className="spacer-right" + height={16} + src={`${getBaseUrl()}/images/alm/github.svg`} + /> + GitHub + </> + ) + }, + { + key: AlmKeys.Bitbucket, + label: ( + <> + <img + alt="bitbucket" + className="spacer-right" + height={16} + src={`${getBaseUrl()}/images/alm/bitbucket.svg`} + /> + Bitbucket Server + </> + ), + requiresBranchesEnabled: true + }, + { + key: AlmKeys.Azure, + label: ( + <> + <img + alt="azure" + className="spacer-right" + height={16} + src={`${getBaseUrl()}/images/alm/azure.svg`} + /> + Azure DevOps Server + </> + ), + requiresBranchesEnabled: true + }, + { + key: AlmKeys.GitLab, + label: ( + <> + <img + alt="gitlab" + className="spacer-right" + height={16} + src={`${getBaseUrl()}/images/alm/gitlab.svg`} + /> + GitLab + </> + ) + } +]; + +export default function AlmIntegrationRenderer(props: AlmIntegrationRendererProps) { + const { + component, + definitionKeyForDeletion, + definitions, + currentAlm, + loading, + branchesEnabled, + multipleAlmEnabled, + projectCount + } = props; + + return ( + <> + <header className="page-header"> + <h1 className="page-title">{translate('settings.almintegration.title')}</h1> + </header> + + <div className="markdown small spacer-top big-spacer-bottom"> + {translate('settings.almintegration.description')} + </div> + <BoxedTabs + onSelect={props.onSelectAlm} + selected={currentAlm} + tabs={tabs.filter(tab => !(tab.requiresBranchesEnabled && !branchesEnabled))} + /> + + {currentAlm === AlmKeys.Azure && ( + <AzureTab + definitions={definitions.azure} + loading={loading} + multipleAlmEnabled={multipleAlmEnabled} + onDelete={props.onDelete} + onUpdateDefinitions={props.onUpdateDefinitions} + /> + )} + {currentAlm === AlmKeys.Bitbucket && ( + <BitbucketTab + definitions={definitions.bitbucket} + loading={loading} + multipleAlmEnabled={multipleAlmEnabled} + onDelete={props.onDelete} + onUpdateDefinitions={props.onUpdateDefinitions} + /> + )} + {currentAlm === AlmKeys.GitHub && ( + <GithubTab + branchesEnabled={branchesEnabled} + component={component} + definitions={definitions.github} + loading={loading} + multipleAlmEnabled={multipleAlmEnabled} + onDelete={props.onDelete} + onUpdateDefinitions={props.onUpdateDefinitions} + /> + )} + {currentAlm === AlmKeys.GitLab && ( + <GitlabTab + branchesEnabled={branchesEnabled} + definitions={definitions.gitlab} + loading={loading} + multipleAlmEnabled={multipleAlmEnabled} + onDelete={props.onDelete} + onUpdateDefinitions={props.onUpdateDefinitions} + /> + )} + + {definitionKeyForDeletion && ( + <DeleteModal + id={definitionKeyForDeletion} + onCancel={props.onCancel} + onDelete={props.onConfirmDelete} + projectCount={projectCount} + /> + )} + </> + ); +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmTab.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmTab.tsx index de3979ea0cb..6bda165b607 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmTab.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmTab.tsx @@ -18,18 +18,22 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { AlmSettingsBinding, ALM_KEYS } from '../../../../types/alm-settings'; -import { AlmPRDecorationFormChildrenProps } from './AlmPRDecorationForm'; +import { AlmBindingDefinition, AlmKeys } from '../../../../types/alm-settings'; +import { AlmBindingDefinitionFormChildrenProps } from './AlmBindingDefinitionForm'; +import { AlmIntegrationFeatureBoxProps } from './AlmIntegrationFeatureBox'; import AlmTabRenderer from './AlmTabRenderer'; interface Props<B> { - alm: ALM_KEYS; + alm: AlmKeys; additionalColumnsHeaders?: string[]; additionalColumnsKeys?: Array<keyof B>; + additionalTableInfo?: React.ReactNode; createConfiguration: (data: B) => Promise<void>; defaultBinding: B; definitions: B[]; - form: (props: AlmPRDecorationFormChildrenProps<B>) => React.ReactNode; + features?: AlmIntegrationFeatureBoxProps[]; + form: (props: AlmBindingDefinitionFormChildrenProps<B>) => React.ReactNode; + help?: React.ReactNode; loading: boolean; multipleAlmEnabled: boolean; onDelete: (definitionKey: string) => void; @@ -43,7 +47,7 @@ interface State<B> { success: boolean; } -export default class AlmTab<B extends AlmSettingsBinding> extends React.PureComponent< +export default class AlmTab<B extends AlmBindingDefinition> extends React.PureComponent< Props<B>, State<B> > { @@ -100,10 +104,13 @@ export default class AlmTab<B extends AlmSettingsBinding> extends React.PureComp const { additionalColumnsHeaders = [], additionalColumnsKeys = [], + additionalTableInfo, alm, defaultBinding, definitions, + features, form, + help, loading, multipleAlmEnabled } = this.props; @@ -113,11 +120,14 @@ export default class AlmTab<B extends AlmSettingsBinding> extends React.PureComp <AlmTabRenderer additionalColumnsHeaders={additionalColumnsHeaders} additionalColumnsKeys={additionalColumnsKeys} + additionalTableInfo={additionalTableInfo} alm={alm} defaultBinding={defaultBinding} definitions={definitions} editedDefinition={editedDefinition} + features={features} form={form} + help={help} loading={loading || submitting} multipleAlmEnabled={multipleAlmEnabled} onCancel={this.handleCancel} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmTabRenderer.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmTabRenderer.tsx new file mode 100644 index 00000000000..15dc454fe9c --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AlmTabRenderer.tsx @@ -0,0 +1,160 @@ +/* + * 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'; +import { FormattedMessage } from 'react-intl'; +import { Link } from 'react-router'; +import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import { AlmBindingDefinition, AlmKeys } from '../../../../types/alm-settings'; +import AlmBindingDefinitionForm, { + AlmBindingDefinitionFormChildrenProps +} from './AlmBindingDefinitionForm'; +import AlmBindingDefinitionsTable from './AlmBindingDefinitionsTable'; +import AlmIntegrationFeatureBox, { + AlmIntegrationFeatureBoxProps +} from './AlmIntegrationFeatureBox'; + +export interface AlmTabRendererProps<B> { + additionalColumnsHeaders: string[]; + additionalColumnsKeys: Array<keyof B>; + additionalTableInfo?: React.ReactNode; + alm: AlmKeys; + editedDefinition?: B; + defaultBinding: B; + definitions: B[]; + features?: AlmIntegrationFeatureBoxProps[]; + form: (props: AlmBindingDefinitionFormChildrenProps<B>) => React.ReactNode; + help?: React.ReactNode; + loading: boolean; + multipleAlmEnabled: boolean; + onCancel: () => void; + onCreate: () => void; + onDelete: (definitionKey: string) => void; + onEdit: (definitionKey: string) => void; + onSubmit: (config: B, originalKey: string) => void; + success: boolean; +} + +export default function AlmTabRenderer<B extends AlmBindingDefinition>( + props: AlmTabRendererProps<B> +) { + const { + additionalColumnsHeaders, + additionalColumnsKeys, + additionalTableInfo, + alm, + defaultBinding, + definitions, + editedDefinition, + features = [], + form, + loading, + multipleAlmEnabled, + success, + help = ( + <FormattedMessage + defaultMessage={translate(`settings.almintegration.${alm}.info`)} + id={`settings.almintegration.${alm}.info`} + values={{ + link: ( + <Link target="_blank" to="/documentation/analysis/pr-decoration/"> + {translate('learn_more')} + </Link> + ) + }} + /> + ) + } = props; + + let definition: B | undefined; + let mappedDefinitions: Array<{ key: string; additionalColumns: string[] }> = []; + let showEdit: boolean | undefined; + + if (!multipleAlmEnabled) { + definition = editedDefinition; + if (definition === undefined && definitions.length > 0) { + definition = definitions[0]; + } + showEdit = definition && editedDefinition === undefined; + } else { + mappedDefinitions = definitions.map(({ key, ...properties }) => { + const additionalColumns = additionalColumnsKeys.map(k => (properties as any)[k]); + return { + key, + additionalColumns + }; + }); + } + + return ( + <div className="big-padded"> + {multipleAlmEnabled ? ( + <DeferredSpinner loading={loading}> + <AlmBindingDefinitionsTable + additionalColumnsHeaders={additionalColumnsHeaders} + additionalTableInfo={additionalTableInfo} + alm={alm} + definitions={mappedDefinitions} + onCreate={props.onCreate} + onDelete={props.onDelete} + onEdit={props.onEdit} + /> + + {editedDefinition && ( + <AlmBindingDefinitionForm + bindingDefinition={editedDefinition} + help={help} + onCancel={props.onCancel} + onSubmit={props.onSubmit} + showInModal={true}> + {form} + </AlmBindingDefinitionForm> + )} + </DeferredSpinner> + ) : ( + <AlmBindingDefinitionForm + bindingDefinition={definition || defaultBinding} + help={help} + hideKeyField={true} + loading={loading} + onCancel={props.onCancel} + onDelete={definition ? props.onDelete : undefined} + onEdit={showEdit ? props.onEdit : undefined} + onSubmit={props.onSubmit} + readOnly={showEdit} + success={success}> + {form} + </AlmBindingDefinitionForm> + )} + + {features.length > 0 && ( + <div className="big-spacer-top big-padded-top bordered-top"> + <h3 className="big-spacer-bottom">{translate('settings.almintegration.features')}</h3> + + <div className="display-flex-wrap"> + {features.map((feature, i) => ( + <AlmIntegrationFeatureBox key={i} {...feature} /> + ))} + </div> + </div> + )} + </div> + ); +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AzureForm.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AzureForm.tsx index 5587f6efafe..fef12180192 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AzureForm.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AzureForm.tsx @@ -20,7 +20,7 @@ import * as React from 'react'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { AzureBindingDefinition } from '../../../../types/alm-settings'; -import { AlmDefinitionFormField } from './AlmDefinitionFormField'; +import { AlmBindingDefinitionFormField } from './AlmBindingDefinitionFormField'; export interface AzureFormProps { formData: AzureBindingDefinition; @@ -35,9 +35,9 @@ export default function AzureForm(props: AzureFormProps) { return ( <> {!hideKeyField && ( - <AlmDefinitionFormField + <AlmBindingDefinitionFormField autoFocus={true} - help={translate('settings.pr_decoration.form.name.azure.help')} + help={translate('settings.almintegration.form.name.azure.help')} id="name.azure" onFieldChange={onFieldChange} propKey="key" @@ -45,8 +45,8 @@ export default function AzureForm(props: AzureFormProps) { value={formData.key} /> )} - <AlmDefinitionFormField - help={translate('settings.pr_decoration.form.personal_access_token.azure.help')} + <AlmBindingDefinitionFormField + help={translate('settings.almintegration.form.personal_access_token.azure.help')} id="personal_access_token" isTextArea={true} onFieldChange={onFieldChange} diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AzureTab.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AzureTab.tsx index ee2711b00de..bf2607a2396 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AzureTab.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/AzureTab.tsx @@ -18,8 +18,9 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import * as React from 'react'; -import { createAzureConfiguration, updateAzureConfiguration } from '../../../../api/almSettings'; -import { ALM_KEYS, AzureBindingDefinition } from '../../../../types/alm-settings'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import { createAzureConfiguration, updateAzureConfiguration } from '../../../../api/alm-settings'; +import { AlmKeys, AzureBindingDefinition } from '../../../../types/alm-settings'; import AlmTab from './AlmTab'; import AzureForm from './AzureForm'; @@ -35,17 +36,27 @@ export default function AzureTab(props: AzureTabProps) { const { multipleAlmEnabled, definitions, loading } = props; return ( - <AlmTab - alm={ALM_KEYS.AZURE} - createConfiguration={createAzureConfiguration} - defaultBinding={{ key: '', personalAccessToken: '' }} - definitions={definitions} - form={childProps => <AzureForm {...childProps} />} - loading={loading} - multipleAlmEnabled={multipleAlmEnabled} - onDelete={props.onDelete} - onUpdateDefinitions={props.onUpdateDefinitions} - updateConfiguration={updateAzureConfiguration} - /> + <div className="bordered"> + <AlmTab + alm={AlmKeys.Azure} + createConfiguration={createAzureConfiguration} + defaultBinding={{ key: '', personalAccessToken: '' }} + definitions={definitions} + features={[ + { + name: translate('settings.almintegration.feature.pr_decoration.title'), + active: definitions.length > 0, + description: translate('settings.almintegration.feature.pr_decoration.description'), + inactiveReason: translate('settings.almintegration.feature.need_at_least_1_binding') + } + ]} + form={childProps => <AzureForm {...childProps} />} + loading={loading} + multipleAlmEnabled={multipleAlmEnabled} + onDelete={props.onDelete} + onUpdateDefinitions={props.onUpdateDefinitions} + updateConfiguration={updateAzureConfiguration} + /> + </div> ); } diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/BitbucketForm.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketForm.tsx index 33b1d93eb88..a306bdbabfd 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/BitbucketForm.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketForm.tsx @@ -21,7 +21,7 @@ import * as React from 'react'; import { FormattedMessage } from 'react-intl'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { BitbucketBindingDefinition } from '../../../../types/alm-settings'; -import { AlmDefinitionFormField } from './AlmDefinitionFormField'; +import { AlmBindingDefinitionFormField } from './AlmBindingDefinitionFormField'; export interface BitbucketFormProps { formData: BitbucketBindingDefinition; @@ -36,9 +36,9 @@ export default function BitbucketForm(props: BitbucketFormProps) { return ( <> {!hideKeyField && ( - <AlmDefinitionFormField + <AlmBindingDefinitionFormField autoFocus={true} - help={translate('settings.pr_decoration.form.name.bitbucket.help')} + help={translate('settings.almintegration.form.name.bitbucket.help')} id="name.bitbucket" maxLength={100} onFieldChange={onFieldChange} @@ -47,11 +47,11 @@ export default function BitbucketForm(props: BitbucketFormProps) { value={formData.key} /> )} - <AlmDefinitionFormField + <AlmBindingDefinitionFormField help={ <FormattedMessage - defaultMessage={translate('settings.pr_decoration.form.url.bitbucket.help')} - id="settings.pr_decoration.form.url.bitbucket.help" + defaultMessage={translate('settings.almintegration.form.url.bitbucket.help')} + id="settings.almintegration.form.url.bitbucket.help" values={{ example: 'https://bitbucket-server.your-company.com' }} /> } @@ -62,7 +62,7 @@ export default function BitbucketForm(props: BitbucketFormProps) { readOnly={readOnly} value={formData.url} /> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField id="personal_access_token" isTextArea={true} onFieldChange={onFieldChange} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketTab.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketTab.tsx new file mode 100644 index 00000000000..e7bf53d56e5 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/BitbucketTab.tsx @@ -0,0 +1,114 @@ +/* + * 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'; +import { FormattedMessage } from 'react-intl'; +import { Link } from 'react-router'; +import { Alert } from 'sonar-ui-common/components/ui/Alert'; +import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n'; +import { + createBitbucketConfiguration, + updateBitbucketConfiguration +} from '../../../../api/alm-settings'; +import { AlmKeys, BitbucketBindingDefinition } from '../../../../types/alm-settings'; +import AlmTab from './AlmTab'; +import BitbucketForm from './BitbucketForm'; + +export interface BitbucketTabProps { + definitions: BitbucketBindingDefinition[]; + loading: boolean; + multipleAlmEnabled: boolean; + onDelete: (definitionKey: string) => void; + onUpdateDefinitions: () => void; +} + +export default function BitbucketTab(props: BitbucketTabProps) { + const { multipleAlmEnabled, definitions, loading } = props; + + return ( + <div className="bordered"> + <AlmTab + additionalColumnsHeaders={[translate('settings.almintegration.table.column.bitbucket.url')]} + additionalColumnsKeys={['url']} + additionalTableInfo={ + <Alert className="big-spacer-bottom width-50" variant="info"> + <FormattedMessage + defaultMessage={translate( + 'settings.almintegration.feature.alm_repo_import.disabled_if_multiple_bbs_instances' + )} + id="settings.almintegration.feature.alm_repo_import.disabled_if_multiple_bbs_instances" + values={{ + feature: ( + <em>{translate('settings.almintegration.feature.alm_repo_import.title')}</em> + ) + }} + /> + </Alert> + } + alm={AlmKeys.Bitbucket} + createConfiguration={createBitbucketConfiguration} + defaultBinding={{ key: '', url: '', personalAccessToken: '' }} + definitions={definitions} + features={[ + { + name: translate('settings.almintegration.feature.pr_decoration.title'), + active: definitions.length > 0, + description: translate('settings.almintegration.feature.pr_decoration.description'), + inactiveReason: translate('settings.almintegration.feature.need_at_least_1_binding') + }, + { + name: translate('settings.almintegration.feature.alm_repo_import.title'), + active: definitions.length === 1, + description: translate('settings.almintegration.feature.alm_repo_import.description'), + inactiveReason: translateWithParameters( + 'onboarding.create_project.too_many_bbs_instances_X', + definitions.length + ) + } + ]} + form={childProps => <BitbucketForm {...childProps} />} + help={ + <> + <h3>{translate('onboarding.create_project.pat_help.title')}</h3> + + <p className="big-spacer-top"> + {translate('settings.almintegration.bitbucket.help_1')} + </p> + + <ul className="big-spacer-top list-styled"> + <li>{translate('settings.almintegration.bitbucket.help_2')}</li> + <li>{translate('settings.almintegration.bitbucket.help_3')}</li> + </ul> + + <p className="big-spacer-top big-spacer-bottom"> + <Link target="_blank" to="/documentation/analysis/pr-decoration/"> + {translate('learn_more')} + </Link> + </p> + </> + } + loading={loading} + multipleAlmEnabled={multipleAlmEnabled} + onDelete={props.onDelete} + onUpdateDefinitions={props.onUpdateDefinitions} + updateConfiguration={updateBitbucketConfiguration} + /> + </div> + ); +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/DeleteModal.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/DeleteModal.tsx index 5d0791ecf47..f4e4bb9da66 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/DeleteModal.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/DeleteModal.tsx @@ -31,11 +31,11 @@ export interface DeleteModalProps { function showProjectCountWarning(projectCount?: number) { if (projectCount === undefined) { - return <p>{translate('settings.pr_decoration.delete.no_info')}</p>; + return <p>{translate('settings.almintegration.delete.no_info')}</p>; } return projectCount ? ( - <p>{translateWithParameters('settings.pr_decoration.delete.info', projectCount)} </p> + <p>{translateWithParameters('settings.almintegration.delete.info', projectCount)} </p> ) : null; } @@ -44,15 +44,15 @@ export default function DeleteModal({ id, onDelete, onCancel, projectCount }: De <ConfirmModal confirmButtonText={translate('delete')} confirmData={id} - header={translate('settings.pr_decoration.delete.header')} + header={translate('settings.almintegration.delete.header')} isDestructive={true} onClose={onCancel} onConfirm={onDelete}> <> <p className="spacer-bottom"> <FormattedMessage - defaultMessage={translate('settings.pr_decoration.delete.message')} - id="settings.pr_decoration.delete.message" + defaultMessage={translate('settings.almintegration.delete.message')} + id="settings.almintegration.delete.message" values={{ id: <b>{id}</b> }} /> </p> diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GithubForm.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GithubForm.tsx index 873cc4aa6c2..c4fd9afee57 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GithubForm.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GithubForm.tsx @@ -20,7 +20,7 @@ import * as React from 'react'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { GithubBindingDefinition } from '../../../../types/alm-settings'; -import { AlmDefinitionFormField } from './AlmDefinitionFormField'; +import { AlmBindingDefinitionFormField } from './AlmBindingDefinitionFormField'; export interface GithubFormProps { formData: GithubBindingDefinition; @@ -35,9 +35,9 @@ export default function GithubForm(props: GithubFormProps) { return ( <> {!hideKeyField && ( - <AlmDefinitionFormField + <AlmBindingDefinitionFormField autoFocus={true} - help={translate('settings.pr_decoration.form.name.github.help')} + help={translate('settings.almintegration.form.name.github.help')} id="name.github" onFieldChange={onFieldChange} propKey="key" @@ -45,15 +45,15 @@ export default function GithubForm(props: GithubFormProps) { value={formData.key} /> )} - <AlmDefinitionFormField + <AlmBindingDefinitionFormField help={ <> - {translate('settings.pr_decoration.form.url.github.help1')} + {translate('settings.almintegration.form.url.github.help1')} <br /> <em>https://github.company.com/api/v3</em> <br /> <br /> - {translate('settings.pr_decoration.form.url.github.help2')} + {translate('settings.almintegration.form.url.github.help2')} <br /> <em>https://api.github.com/</em> </> @@ -65,7 +65,7 @@ export default function GithubForm(props: GithubFormProps) { readOnly={readOnly} value={formData.url} /> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField id="app_id" maxLength={80} onFieldChange={onFieldChange} @@ -73,7 +73,7 @@ export default function GithubForm(props: GithubFormProps) { readOnly={readOnly} value={formData.appId} /> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField id="private_key" isTextArea={true} onFieldChange={onFieldChange} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GithubTab.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GithubTab.tsx new file mode 100644 index 00000000000..cf9aa3c5cb7 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GithubTab.tsx @@ -0,0 +1,85 @@ +/* + * 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'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import { createGithubConfiguration, updateGithubConfiguration } from '../../../../api/alm-settings'; +import { AlmKeys, GithubBindingDefinition } from '../../../../types/alm-settings'; +import { ALM_INTEGRATION } from '../AdditionalCategoryKeys'; +import CategoryDefinitionsList from '../CategoryDefinitionsList'; +import AlmTab from './AlmTab'; +import GithubForm from './GithubForm'; + +export interface GithubTabProps { + branchesEnabled: boolean; + component?: T.Component; + definitions: GithubBindingDefinition[]; + loading: boolean; + multipleAlmEnabled: boolean; + onDelete: (definitionKey: string) => void; + onUpdateDefinitions: () => void; +} + +export default function GithubTab(props: GithubTabProps) { + const { branchesEnabled, component, multipleAlmEnabled, definitions, loading } = props; + + return ( + <div className="bordered"> + {branchesEnabled && ( + <> + <AlmTab + additionalColumnsHeaders={[ + translate('settings.almintegration.table.column.github.url'), + translate('settings.almintegration.table.column.app_id') + ]} + additionalColumnsKeys={['appId', 'url']} + alm={AlmKeys.GitHub} + createConfiguration={createGithubConfiguration} + defaultBinding={{ key: '', appId: '', url: '', privateKey: '' }} + definitions={definitions} + features={[ + { + name: translate('settings.almintegration.feature.pr_decoration.title'), + active: definitions.length > 0, + description: translate('settings.almintegration.feature.pr_decoration.description'), + inactiveReason: translate('settings.almintegration.feature.need_at_least_1_binding') + } + ]} + form={childProps => <GithubForm {...childProps} />} + loading={loading} + multipleAlmEnabled={multipleAlmEnabled} + onDelete={props.onDelete} + onUpdateDefinitions={props.onUpdateDefinitions} + updateConfiguration={updateGithubConfiguration} + /> + + <div className="huge-spacer-top huge-spacer-bottom bordered-top" /> + </> + )} + + <div className="big-padded"> + <CategoryDefinitionsList + category={ALM_INTEGRATION} + component={component} + subCategory={AlmKeys.GitHub} + /> + </div> + </div> + ); +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GitlabForm.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GitlabForm.tsx index 929ad3c5aa4..65eabba516d 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GitlabForm.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GitlabForm.tsx @@ -20,7 +20,7 @@ import * as React from 'react'; import { translate } from 'sonar-ui-common/helpers/l10n'; import { GitlabBindingDefinition } from '../../../../types/alm-settings'; -import { AlmDefinitionFormField } from './AlmDefinitionFormField'; +import { AlmBindingDefinitionFormField } from './AlmBindingDefinitionFormField'; export interface GitlabFormProps { formData: GitlabBindingDefinition; @@ -35,9 +35,9 @@ export default function GitlabForm(props: GitlabFormProps) { return ( <> {!hideKeyField && ( - <AlmDefinitionFormField + <AlmBindingDefinitionFormField autoFocus={true} - help={translate('settings.pr_decoration.form.name.gitlab.help')} + help={translate('settings.almintegration.form.name.gitlab.help')} id="name.gitlab" onFieldChange={onFieldChange} propKey="key" @@ -45,8 +45,8 @@ export default function GitlabForm(props: GitlabFormProps) { value={formData.key} /> )} - <AlmDefinitionFormField - help={translate('settings.pr_decoration.form.personal_access_token.gitlab.help')} + <AlmBindingDefinitionFormField + help={translate('settings.almintegration.form.personal_access_token.gitlab.help')} id="personal_access_token" isTextArea={true} onFieldChange={onFieldChange} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GitlabTab.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GitlabTab.tsx new file mode 100644 index 00000000000..fab99f0ca6b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/GitlabTab.tsx @@ -0,0 +1,80 @@ +/* + * 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'; +import { translate } from 'sonar-ui-common/helpers/l10n'; +import { createGitlabConfiguration, updateGitlabConfiguration } from '../../../../api/alm-settings'; +import { AlmKeys, GitlabBindingDefinition } from '../../../../types/alm-settings'; +import { ALM_INTEGRATION } from '../AdditionalCategoryKeys'; +import CategoryDefinitionsList from '../CategoryDefinitionsList'; +import AlmTab from './AlmTab'; +import GitlabForm from './GitlabForm'; + +export interface GitlabTabProps { + branchesEnabled: boolean; + component?: T.Component; + definitions: GitlabBindingDefinition[]; + loading: boolean; + multipleAlmEnabled: boolean; + onDelete: (definitionKey: string) => void; + onUpdateDefinitions: () => void; +} + +export default function GitlabTab(props: GitlabTabProps) { + const { branchesEnabled, component, multipleAlmEnabled, definitions, loading } = props; + + return ( + <div className="bordered"> + {branchesEnabled && ( + <> + <AlmTab + alm={AlmKeys.GitLab} + createConfiguration={createGitlabConfiguration} + defaultBinding={{ key: '', personalAccessToken: '' }} + definitions={definitions} + features={[ + { + name: translate('settings.almintegration.feature.mr_decoration.title'), + active: definitions.length > 0, + description: translate('settings.almintegration.feature.mr_decoration.description'), + inactiveReason: translate('settings.almintegration.feature.need_at_least_1_binding') + } + ]} + form={childProps => <GitlabForm {...childProps} />} + loading={loading} + multipleAlmEnabled={multipleAlmEnabled} + onDelete={props.onDelete} + onUpdateDefinitions={props.onUpdateDefinitions} + updateConfiguration={updateGitlabConfiguration} + /> + + <div className="huge-spacer-top huge-spacer-bottom bordered-top" /> + </> + )} + + <div className="big-padded"> + <CategoryDefinitionsList + category={ALM_INTEGRATION} + component={component} + subCategory={AlmKeys.GitLab} + /> + </div> + </div> + ); +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmPRDecorationForm-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionForm-test.tsx index c3170ef2f31..c8976eefe0f 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmPRDecorationForm-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionForm-test.tsx @@ -21,8 +21,8 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; import { mockGithubDefinition } from '../../../../../helpers/mocks/alm-settings'; -import { ALM_KEYS, GithubBindingDefinition } from '../../../../../types/alm-settings'; -import AlmPRDecorationForm from '../AlmPRDecorationForm'; +import { GithubBindingDefinition } from '../../../../../types/alm-settings'; +import AlmBindingDefinitionForm from '../AlmBindingDefinitionForm'; it('should render correctly', () => { expect(shallowRender()).toMatchSnapshot(); @@ -121,15 +121,16 @@ it('should (dis)allow submit by validating its state', () => { expect(wrapper.instance().canSubmit()).toBe(true); }); -function shallowRender(props: Partial<AlmPRDecorationForm<GithubBindingDefinition>['props']> = {}) { - return shallow<AlmPRDecorationForm<GithubBindingDefinition>>( - <AlmPRDecorationForm - alm={ALM_KEYS.GITHUB} +function shallowRender( + props: Partial<AlmBindingDefinitionForm<GithubBindingDefinition>['props']> = {} +) { + return shallow<AlmBindingDefinitionForm<GithubBindingDefinition>>( + <AlmBindingDefinitionForm bindingDefinition={{ appId: '', key: '', privateKey: '', url: '' }} onCancel={jest.fn()} onSubmit={jest.fn()} {...props}> {() => null} - </AlmPRDecorationForm> + </AlmBindingDefinitionForm> ); } diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmDefinitionFormField-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionFormField-test.tsx index 5f25326e5f5..6193e77cdbd 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmDefinitionFormField-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionFormField-test.tsx @@ -19,8 +19,11 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; -import { AlmSettingsBinding } from '../../../../../types/alm-settings'; -import { AlmDefinitionFormField, AlmDefinitionFormFieldProps } from '../AlmDefinitionFormField'; +import { AlmBindingDefinition } from '../../../../../types/alm-settings'; +import { + AlmBindingDefinitionFormField, + AlmBindingDefinitionFormFieldProps +} from '../AlmBindingDefinitionFormField'; it('should render correctly', () => { expect(shallowRender()).toMatchSnapshot(); @@ -42,9 +45,11 @@ it('should call onFieldChange', () => { expect(onTextAreaChange).toBeCalled(); }); -function shallowRender(props: Partial<AlmDefinitionFormFieldProps<AlmSettingsBinding>> = {}) { +function shallowRender( + props: Partial<AlmBindingDefinitionFormFieldProps<AlmBindingDefinition>> = {} +) { return shallow( - <AlmDefinitionFormField + <AlmBindingDefinitionFormField id="key" isTextArea={false} maxLength={40} diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmPRDecorationFormModalRenderer-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionFormModalRenderer-test.tsx index a1a6a4a2ff5..3095bd43df8 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmPRDecorationFormModalRenderer-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionFormModalRenderer-test.tsx @@ -19,26 +19,24 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; -import { ALM_KEYS } from '../../../../../types/alm-settings'; -import AlmPRDecorationFormModalRenderer, { - AlmPRDecorationFormModalProps -} from '../AlmPRDecorationFormModalRenderer'; +import AlmBindingDefinitionFormModalRenderer, { + AlmBindingDefinitionFormModalProps +} from '../AlmBindingDefinitionFormModalRenderer'; it('should render correctly', () => { expect(shallowRender().dive()).toMatchSnapshot(); expect(shallowRender({ help: <span>Help me</span> }).dive()).toMatchSnapshot(); }); -function shallowRender(props: Partial<AlmPRDecorationFormModalProps> = {}) { +function shallowRender(props: Partial<AlmBindingDefinitionFormModalProps> = {}) { return shallow( - <AlmPRDecorationFormModalRenderer + <AlmBindingDefinitionFormModalRenderer action="create" - alm={ALM_KEYS.GITHUB} canSubmit={jest.fn()} onCancel={jest.fn()} onSubmit={jest.fn()} {...props}> {() => null} - </AlmPRDecorationFormModalRenderer> + </AlmBindingDefinitionFormModalRenderer> ); } diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmPRDecorationFormRenderer-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionFormRenderer-test.tsx index 1a3f9361430..6774ed436d6 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmPRDecorationFormRenderer-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionFormRenderer-test.tsx @@ -21,9 +21,9 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { SubmitButton } from 'sonar-ui-common/components/controls/buttons'; import { submit } from 'sonar-ui-common/helpers/testUtils'; -import AlmPRDecorationFormRenderer, { - AlmPRDecorationFormRendererProps -} from '../AlmPRDecorationFormRenderer'; +import AlmBindingDefinitionFormRenderer, { + AlmBindingDefinitionFormRendererProps +} from '../AlmBindingDefinitionFormRenderer'; it('should render correctly', () => { expect(shallowRender()).toMatchSnapshot(); @@ -54,15 +54,15 @@ it('should correctly submit the form', () => { expect(onSubmit).toBeCalled(); }); -function shallowRender(props: Partial<AlmPRDecorationFormRendererProps> = {}) { +function shallowRender(props: Partial<AlmBindingDefinitionFormRendererProps> = {}) { return shallow( - <AlmPRDecorationFormRenderer + <AlmBindingDefinitionFormRenderer canSubmit={jest.fn()} loading={false} onSubmit={jest.fn()} success={false} {...props}> {() => null} - </AlmPRDecorationFormRenderer> + </AlmBindingDefinitionFormRenderer> ); } diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmPRDecorationTable-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionsTable-test.tsx index a1f591f1ec9..b75674c56c2 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmPRDecorationTable-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmBindingDefinitionsTable-test.tsx @@ -19,22 +19,24 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; -import { ALM_KEYS } from '../../../../../types/alm-settings'; -import AlmPRDecorationTable, { AlmPRDecorationTableProps } from '../AlmPRDecorationTable'; +import { AlmKeys } from '../../../../../types/alm-settings'; +import AlmBindingDefinitionsTable, { + AlmBindingDefinitionsTableProps +} from '../AlmBindingDefinitionsTable'; it('should render correctly', () => { expect(shallowRender()).toMatchSnapshot(); expect( shallowRender({ additionalColumnsHeaders: ['additional1', 'additional2'], - alm: ALM_KEYS.GITHUB, + alm: AlmKeys.GitHub, definitions: [ { key: 'definition1', additionalColumns: ['def1-v1', 'def1-v2'] }, { key: 'definition2', additionalColumns: ['def2-v1', 'def2-v2'] } ] }) ).toMatchSnapshot('additional columns'); - expect(shallowRender({ alm: ALM_KEYS.GITHUB })).toMatchSnapshot('title adjusts for GitLab'); + expect(shallowRender({ alm: AlmKeys.GitLab })).toMatchSnapshot('title adjusts for GitLab'); }); it('should correctly trigger create, delete, and edit', () => { @@ -44,7 +46,7 @@ it('should correctly trigger create, delete, and edit', () => { const wrapper = shallowRender({ additionalColumnsHeaders: [], - alm: ALM_KEYS.BITBUCKET, + alm: AlmKeys.Bitbucket, definitions: [{ key: 'defKey', additionalColumns: [] }], onCreate, onDelete, @@ -61,11 +63,11 @@ it('should correctly trigger create, delete, and edit', () => { expect(onEdit).toBeCalledWith('defKey'); }); -function shallowRender(props: Partial<AlmPRDecorationTableProps> = {}) { +function shallowRender(props: Partial<AlmBindingDefinitionsTableProps> = {}) { return shallow( - <AlmPRDecorationTable + <AlmBindingDefinitionsTable additionalColumnsHeaders={[]} - alm={ALM_KEYS.AZURE} + alm={AlmKeys.Azure} definitions={[]} onCreate={jest.fn()} onDelete={jest.fn()} diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/PullRequestDecoration-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegration-test.tsx index ce41ff990d1..1e6a791f8b5 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/PullRequestDecoration-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegration-test.tsx @@ -24,11 +24,11 @@ import { countBindedProjects, deleteConfiguration, getAlmDefinitions -} from '../../../../../api/almSettings'; -import { ALM_KEYS } from '../../../../../types/alm-settings'; -import PullRequestDecoration from '../PullRequestDecoration'; +} from '../../../../../api/alm-settings'; +import { AlmKeys } from '../../../../../types/alm-settings'; +import { AlmIntegration } from '../AlmIntegration'; -jest.mock('../../../../../api/almSettings', () => ({ +jest.mock('../../../../../api/alm-settings', () => ({ countBindedProjects: jest.fn().mockResolvedValue(0), deleteConfiguration: jest.fn().mockResolvedValue(undefined), getAlmDefinitions: jest.fn().mockResolvedValue({ github: [] }) @@ -45,13 +45,13 @@ it('should render correctly', () => { it('should handle alm selection', async () => { const wrapper = shallowRender(); - wrapper.setState({ currentAlm: ALM_KEYS.AZURE }); + wrapper.setState({ currentAlm: AlmKeys.Azure }); - wrapper.instance().handleSelectAlm(ALM_KEYS.GITHUB); + wrapper.instance().handleSelectAlm(AlmKeys.GitHub); await waitAndUpdate(wrapper); - expect(wrapper.state().currentAlm).toBe(ALM_KEYS.GITHUB); + expect(wrapper.state().currentAlm).toBe(AlmKeys.GitHub); }); it('should handle delete', async () => { @@ -90,5 +90,5 @@ it('should fetch settings', async () => { }); function shallowRender() { - return shallow<PullRequestDecoration>(<PullRequestDecoration />); + return shallow<AlmIntegration>(<AlmIntegration appState={{ branchesEnabled: true }} />); } diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegrationFeatureBox-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegrationFeatureBox-test.tsx new file mode 100644 index 00000000000..7366117e6bb --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegrationFeatureBox-test.tsx @@ -0,0 +1,39 @@ +/* + * 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 { shallow } from 'enzyme'; +import * as React from 'react'; +import AlmIntegrationFeatureBox, { + AlmIntegrationFeatureBoxProps +} from '../AlmIntegrationFeatureBox'; + +it('should render correctly', () => { + expect(shallowRender()).toMatchSnapshot('default'); + expect(shallowRender({ active: false })).toMatchSnapshot('inactive'); + expect(shallowRender({ active: false, inactiveReason: "Bar is foo'd" })).toMatchSnapshot( + 'inactive, with reason' + ); +}); + +function shallowRender(props: Partial<AlmIntegrationFeatureBoxProps> = {}) { + return shallow<AlmIntegrationFeatureBoxProps>( + <AlmIntegrationFeatureBox active={true} description="Foo bar..." name="Foo" {...props} /> + ); +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/PRDecorationTabs-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegrationRenderer-test.tsx index 4ed69a4feb1..1752860997f 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/PRDecorationTabs-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmIntegrationRenderer-test.tsx @@ -19,24 +19,28 @@ */ import { shallow } from 'enzyme'; import * as React from 'react'; -import { ALM_KEYS } from '../../../../../types/alm-settings'; -import { PRDecorationTabs, PRDecorationTabsProps } from '../PRDecorationTabs'; +import { AlmKeys } from '../../../../../types/alm-settings'; +import AlmIntegrationRenderer, { AlmIntegrationRendererProps } from '../AlmIntegrationRenderer'; it('should render correctly', () => { - expect(shallowRender({ loading: true })).toMatchSnapshot(); - expect(shallowRender({ definitionKeyForDeletion: 'keyToDelete' })).toMatchSnapshot(); - expect(shallowRender({ currentAlm: ALM_KEYS.AZURE })).toMatchSnapshot(); - expect(shallowRender({ currentAlm: ALM_KEYS.BITBUCKET })).toMatchSnapshot(); - expect(shallowRender({ currentAlm: ALM_KEYS.GITLAB })).toMatchSnapshot(); + expect(shallowRender()).toMatchSnapshot('default'); + expect(shallowRender({ loading: true })).toMatchSnapshot('loading'); + expect(shallowRender({ definitionKeyForDeletion: 'keyToDelete' })).toMatchSnapshot( + 'delete modal' + ); + expect(shallowRender({ currentAlm: AlmKeys.Azure })).toMatchSnapshot('azure'); + expect(shallowRender({ currentAlm: AlmKeys.Bitbucket })).toMatchSnapshot('bitbucket'); + expect(shallowRender({ currentAlm: AlmKeys.GitLab })).toMatchSnapshot('gitlab'); }); -function shallowRender(props: Partial<PRDecorationTabsProps> = {}) { +function shallowRender(props: Partial<AlmIntegrationRendererProps> = {}) { return shallow( - <PRDecorationTabs - appState={{ multipleAlmEnabled: false }} - currentAlm={ALM_KEYS.GITHUB} + <AlmIntegrationRenderer + branchesEnabled={true} + currentAlm={AlmKeys.GitHub} definitions={{ azure: [], bitbucket: [], github: [], gitlab: [] }} loading={false} + multipleAlmEnabled={false} onCancel={jest.fn()} onConfirmDelete={jest.fn()} onDelete={jest.fn()} diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmTab-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmTab-test.tsx index 89969e53fef..4e6fb6289d0 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmTab-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmTab-test.tsx @@ -21,7 +21,7 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; import { mockAzureDefinition } from '../../../../../helpers/mocks/alm-settings'; -import { ALM_KEYS, AzureBindingDefinition } from '../../../../../types/alm-settings'; +import { AlmKeys, AzureBindingDefinition } from '../../../../../types/alm-settings'; import AlmTab from '../AlmTab'; const DEFAULT_BINDING = { @@ -93,7 +93,7 @@ it('should update config', async () => { function shallowRender(props: Partial<AlmTab<AzureBindingDefinition>['props']> = {}) { return shallow<AlmTab<AzureBindingDefinition>>( <AlmTab - alm={ALM_KEYS.AZURE} + alm={AlmKeys.Azure} createConfiguration={jest.fn()} defaultBinding={DEFAULT_BINDING} definitions={[mockAzureDefinition()]} diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmTabRenderer-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmTabRenderer-test.tsx index 0eeb1fe2d7b..d0fab91181d 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmTabRenderer-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AlmTabRenderer-test.tsx @@ -20,7 +20,7 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import { mockGithubDefinition } from '../../../../../helpers/mocks/alm-settings'; -import { ALM_KEYS, GithubBindingDefinition } from '../../../../../types/alm-settings'; +import { AlmKeys, GithubBindingDefinition } from '../../../../../types/alm-settings'; import AlmTabRenderer, { AlmTabRendererProps } from '../AlmTabRenderer'; it('should render correctly for multi-ALM binding', () => { @@ -29,6 +29,22 @@ it('should render correctly for multi-ALM binding', () => { expect(shallowRender({ editedDefinition: mockGithubDefinition() })).toMatchSnapshot( 'editing a definition' ); + expect( + shallowRender({ + features: [ + { + active: true, + name: 'Foo', + description: 'Bar' + }, + { + active: false, + name: 'Baz', + description: 'Bim' + } + ] + }) + ).toMatchSnapshot('with features'); }); it('should render correctly for single-ALM binding', () => { @@ -44,7 +60,7 @@ function shallowRender(props: Partial<AlmTabRendererProps<GithubBindingDefinitio <AlmTabRenderer additionalColumnsHeaders={['url', 'app_id']} additionalColumnsKeys={['url', 'appId']} - alm={ALM_KEYS.GITHUB} + alm={AlmKeys.GitHub} defaultBinding={mockGithubDefinition()} definitions={[mockGithubDefinition()]} form={jest.fn()} diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AzureForm-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AzureForm-test.tsx index 1d5fbb7472b..1d5fbb7472b 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AzureForm-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AzureForm-test.tsx diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AzureTab-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AzureTab-test.tsx index 16dba8d81cd..16dba8d81cd 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AzureTab-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/AzureTab-test.tsx diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/BitbucketForm-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketForm-test.tsx index de693816844..de693816844 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/BitbucketForm-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketForm-test.tsx diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/BitbucketTab-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketTab-test.tsx index 1cdb3162ad9..1cdb3162ad9 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/BitbucketTab-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/BitbucketTab-test.tsx diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/DeleteModal-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/DeleteModal-test.tsx index dc6292d7cd6..dc6292d7cd6 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/DeleteModal-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/DeleteModal-test.tsx diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GithubForm-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/GithubForm-test.tsx index b279b01ea22..b279b01ea22 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GithubForm-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/GithubForm-test.tsx diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GithubTab-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/GithubTab-test.tsx index 43a689bc702..15d6d36638d 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GithubTab-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/GithubTab-test.tsx @@ -23,12 +23,14 @@ import { mockGithubDefinition } from '../../../../../helpers/mocks/alm-settings' import GithubTab, { GithubTabProps } from '../GithubTab'; it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot(); + expect(shallowRender()).toMatchSnapshot('with branch support'); + expect(shallowRender({ branchesEnabled: false })).toMatchSnapshot('without branch support'); }); function shallowRender(props: Partial<GithubTabProps> = {}) { return shallow( <GithubTab + branchesEnabled={true} definitions={[mockGithubDefinition()]} loading={false} multipleAlmEnabled={true} diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GitlabForm-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/GitlabForm-test.tsx index 038331bd641..038331bd641 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GitlabForm-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/GitlabForm-test.tsx diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GitlabTab-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/GitlabTab-test.tsx index 0325f1b3161..45cc16ed97b 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GitlabTab-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/GitlabTab-test.tsx @@ -23,12 +23,14 @@ import { mockGitlabDefinition } from '../../../../../helpers/mocks/alm-settings' import GitlabTab, { GitlabTabProps } from '../GitlabTab'; it('should render correctly', () => { - expect(shallowRender()).toMatchSnapshot(); + expect(shallowRender()).toMatchSnapshot('with branch support'); + expect(shallowRender({ branchesEnabled: false })).toMatchSnapshot('without branch support'); }); function shallowRender(props: Partial<GitlabTabProps> = {}) { return shallow( <GitlabTab + branchesEnabled={true} definitions={[mockGitlabDefinition()]} loading={false} multipleAlmEnabled={true} diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmPRDecorationForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionForm-test.tsx.snap index 98dbf7f493c..58dbf7ad3e5 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmPRDecorationForm-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionForm-test.tsx.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should render correctly 1`] = ` -<AlmPRDecorationFormRenderer +<AlmBindingDefinitionFormRenderer canSubmit={[Function]} loading={false} onCancel={[Function]} diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmDefinitionFormField-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionFormField-test.tsx.snap index 57a15af1578..7420fe8fc6c 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmDefinitionFormField-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionFormField-test.tsx.snap @@ -8,7 +8,7 @@ exports[`should render correctly 1`] = ` className="display-flex-center" htmlFor="key" > - settings.pr_decoration.form.key + settings.almintegration.form.key <em className="mandatory spacer-right" > @@ -37,7 +37,7 @@ exports[`should render correctly 2`] = ` className="display-flex-center" htmlFor="key" > - settings.pr_decoration.form.key + settings.almintegration.form.key <em className="mandatory spacer-right" > @@ -70,7 +70,7 @@ exports[`should render correctly 3`] = ` className="display-flex-center" htmlFor="key" > - settings.pr_decoration.form.key + settings.almintegration.form.key <em className="mandatory spacer-right" > diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmPRDecorationFormModalRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionFormModalRenderer-test.tsx.snap index 8c73cbf6880..acd27e45bd0 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmPRDecorationFormModalRenderer-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionFormModalRenderer-test.tsx.snap @@ -2,7 +2,7 @@ exports[`should render correctly 1`] = ` <Modal - contentLabel="settings.pr_decoration.form.header.create" + contentLabel="settings.almintegration.form.header.create" onRequestClose={[MockFunction]} size="medium" > @@ -14,13 +14,21 @@ exports[`should render correctly 1`] = ` className="modal-head" > <h2> - settings.pr_decoration.form.header.create + settings.almintegration.form.header.create </h2> </div> <div className="modal-body modal-container" > - <Component /> + <div + className="display-flex-start" + > + <div + className="flex-1" + > + <Component /> + </div> + </div> </div> <div className="modal-foot" @@ -33,7 +41,7 @@ exports[`should render correctly 1`] = ` <SubmitButton disabled={true} > - settings.pr_decoration.form.save + settings.almintegration.form.save </SubmitButton> <ResetButtonLink onClick={[Function]} @@ -47,7 +55,7 @@ exports[`should render correctly 1`] = ` exports[`should render correctly 2`] = ` <Modal - contentLabel="settings.pr_decoration.form.header.create" + contentLabel="settings.almintegration.form.header.create" onRequestClose={[MockFunction]} size="medium" > @@ -59,21 +67,29 @@ exports[`should render correctly 2`] = ` className="modal-head" > <h2> - settings.pr_decoration.form.header.create + settings.almintegration.form.header.create </h2> </div> <div className="modal-body modal-container" > - <Alert - className="big-spacer-bottom" - variant="info" + <div + className="display-flex-start" > - <span> - Help me - </span> - </Alert> - <Component /> + <div + className="flex-1" + > + <Component /> + </div> + <Alert + className="huge-spacer-left flex-1" + variant="info" + > + <span> + Help me + </span> + </Alert> + </div> </div> <div className="modal-foot" @@ -86,7 +102,7 @@ exports[`should render correctly 2`] = ` <SubmitButton disabled={true} > - settings.pr_decoration.form.save + settings.almintegration.form.save </SubmitButton> <ResetButtonLink onClick={[Function]} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionFormRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionFormRenderer-test.tsx.snap new file mode 100644 index 00000000000..8d7b47e2d63 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionFormRenderer-test.tsx.snap @@ -0,0 +1,161 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<form + className="views-form" + data-test="settings__alm-form" + onSubmit={[Function]} +> + <div + className="display-flex-start" + > + <div + className="flex-1" + > + <Component /> + <div + className="display-flex-center" + > + <SubmitButton + disabled={true} + > + settings.almintegration.form.save + </SubmitButton> + </div> + </div> + </div> +</form> +`; + +exports[`should render correctly 2`] = ` +<form + className="views-form" + data-test="settings__alm-form" + onSubmit={[Function]} +> + <div + className="display-flex-start" + > + <div + className="flex-1" + > + <Component /> + <div + className="display-flex-center" + > + <SubmitButton + disabled={true} + > + settings.almintegration.form.save + </SubmitButton> + <ResetButtonLink + className="spacer-left" + onClick={[MockFunction]} + > + cancel + </ResetButtonLink> + </div> + </div> + </div> +</form> +`; + +exports[`should render correctly 3`] = ` +<form + className="views-form" + data-test="settings__alm-form" + onSubmit={[Function]} +> + <div + className="display-flex-start" + > + <div + className="flex-1" + > + <Component /> + <div + className="display-flex-center" + > + <SubmitButton + disabled={true} + > + settings.almintegration.form.save + </SubmitButton> + <Button + className="button-red spacer-left" + disabled={false} + onClick={[MockFunction]} + > + delete + </Button> + </div> + </div> + </div> +</form> +`; + +exports[`should render correctly 4`] = ` +<form + className="views-form" + data-test="settings__alm-form" + onSubmit={[Function]} +> + <div + className="display-flex-start" + > + <div + className="flex-1" + > + <Component /> + <div + className="display-flex-center" + > + <SubmitButton + disabled={true} + > + settings.almintegration.form.save + </SubmitButton> + <span + className="text-success spacer-left" + > + <AlertSuccessIcon + className="spacer-right" + /> + settings.state.saved + </span> + </div> + </div> + </div> +</form> +`; + +exports[`should render correctly 5`] = ` +<form + className="views-form" + data-test="settings__alm-form" + onSubmit={[Function]} +> + <div + className="display-flex-start" + > + <div + className="flex-1" + > + <Component /> + <div + className="display-flex-center" + > + <SubmitButton + disabled={true} + > + settings.almintegration.form.save + </SubmitButton> + <DeferredSpinner + className="spacer-left" + timeout={100} + /> + </div> + </div> + </div> +</form> +`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmPRDecorationTable-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionsTable-test.tsx.snap index bc850412321..ff5dc35a333 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmPRDecorationTable-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmBindingDefinitionsTable-test.tsx.snap @@ -5,16 +5,16 @@ exports[`should render correctly 1`] = ` <div className="spacer-top big-spacer-bottom display-flex-space-between" > - <h4 - className="display-inline" + <h2 + className="settings-sub-category-name" > - settings.pr_decoration.table.title - </h4> + settings.almintegration.table.title + </h2> <Button data-test="settings__alm-create" onClick={[MockFunction]} > - settings.pr_decoration.table.create + settings.almintegration.table.create </Button> </div> <table @@ -23,17 +23,17 @@ exports[`should render correctly 1`] = ` <thead> <tr> <th> - settings.pr_decoration.table.column.name + settings.almintegration.table.column.name </th> <th className="action-small text-center" > - settings.pr_decoration.table.column.edit + settings.almintegration.table.column.edit </th> <th className="action text-center" > - settings.pr_decoration.table.column.delete + settings.almintegration.table.column.delete </th> </tr> </thead> @@ -44,7 +44,7 @@ exports[`should render correctly 1`] = ` <td colSpan={3} > - settings.pr_decoration.table.empty.azure + settings.almintegration.table.empty.azure </td> </tr> </tbody> @@ -57,16 +57,16 @@ exports[`should render correctly: additional columns 1`] = ` <div className="spacer-top big-spacer-bottom display-flex-space-between" > - <h4 - className="display-inline" + <h2 + className="settings-sub-category-name" > - settings.pr_decoration.table.title - </h4> + settings.almintegration.table.title + </h2> <Button data-test="settings__alm-create" onClick={[MockFunction]} > - settings.pr_decoration.table.create + settings.almintegration.table.create </Button> </div> <table @@ -75,7 +75,7 @@ exports[`should render correctly: additional columns 1`] = ` <thead> <tr> <th> - settings.pr_decoration.table.column.name + settings.almintegration.table.column.name </th> <th key="additional1" @@ -90,12 +90,12 @@ exports[`should render correctly: additional columns 1`] = ` <th className="action-small text-center" > - settings.pr_decoration.table.column.edit + settings.almintegration.table.column.edit </th> <th className="action text-center" > - settings.pr_decoration.table.column.delete + settings.almintegration.table.column.delete </th> </tr> </thead> @@ -196,16 +196,16 @@ exports[`should render correctly: title adjusts for GitLab 1`] = ` <div className="spacer-top big-spacer-bottom display-flex-space-between" > - <h4 - className="display-inline" + <h2 + className="settings-sub-category-name" > - settings.pr_decoration.table.title - </h4> + settings.almintegration.table.title + </h2> <Button data-test="settings__alm-create" onClick={[MockFunction]} > - settings.pr_decoration.table.create + settings.almintegration.table.create </Button> </div> <table @@ -214,17 +214,17 @@ exports[`should render correctly: title adjusts for GitLab 1`] = ` <thead> <tr> <th> - settings.pr_decoration.table.column.name + settings.almintegration.table.column.name </th> <th className="action-small text-center" > - settings.pr_decoration.table.column.edit + settings.almintegration.table.column.edit </th> <th className="action text-center" > - settings.pr_decoration.table.column.delete + settings.almintegration.table.column.delete </th> </tr> </thead> @@ -235,7 +235,7 @@ exports[`should render correctly: title adjusts for GitLab 1`] = ` <td colSpan={3} > - settings.pr_decoration.table.empty.github + settings.almintegration.table.empty.gitlab </td> </tr> </tbody> diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/PullRequestDecoration-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmIntegration-test.tsx.snap index d41b9e0d342..a056a472a84 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/PullRequestDecoration-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmIntegration-test.tsx.snap @@ -1,7 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should render correctly 1`] = ` -<Connect(withAppState(PRDecorationTabs)) +<AlmIntegrationRenderer + branchesEnabled={true} currentAlm="github" definitions={ Object { @@ -12,6 +13,7 @@ exports[`should render correctly 1`] = ` } } loading={true} + multipleAlmEnabled={false} onCancel={[Function]} onConfirmDelete={[Function]} onDelete={[Function]} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmIntegrationFeatureBox-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmIntegrationFeatureBox-test.tsx.snap new file mode 100644 index 00000000000..16a177214ae --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmIntegrationFeatureBox-test.tsx.snap @@ -0,0 +1,97 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly: default 1`] = ` +<div + className="boxed-group-inner display-flex-start width-30 spacer-right spacer-bottom bordered" +> + <CheckIcon + className="little-spacer-top spacer-right" + fill="#00aa00" + /> + <div + className="display-flex-column abs-height-100" + > + <h4> + Foo + </h4> + <div + className="spacer-top flex-1" + > + Foo bar... + </div> + <div + className="spacer-top" + > + <em + className="text-success" + > + settings.almintegration.feature.enabled + </em> + </div> + </div> +</div> +`; + +exports[`should render correctly: inactive 1`] = ` +<div + className="boxed-group-inner display-flex-start width-30 spacer-right spacer-bottom bordered bg-muted" +> + <ClearIcon + className="little-spacer-top spacer-right" + fill="#999" + /> + <div + className="display-flex-column abs-height-100" + > + <h4> + Foo + </h4> + <div + className="spacer-top flex-1" + > + Foo bar... + </div> + <div + className="spacer-top" + > + <em + className="text-muted" + > + settings.almintegration.feature.disabled + </em> + </div> + </div> +</div> +`; + +exports[`should render correctly: inactive, with reason 1`] = ` +<div + className="boxed-group-inner display-flex-start width-30 spacer-right spacer-bottom bordered bg-muted" +> + <ClearIcon + className="little-spacer-top spacer-right" + fill="#999" + /> + <div + className="display-flex-column abs-height-100" + > + <h4> + Foo + </h4> + <div + className="spacer-top flex-1" + > + Foo bar... + </div> + <div + className="spacer-top" + > + <em + className="text-muted" + > + Bar is foo'd + </em> + </div> + </div> +</div> +`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/PRDecorationTabs-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmIntegrationRenderer-test.tsx.snap index 5590bf87bcb..62430cb1471 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/PRDecorationTabs-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmIntegrationRenderer-test.tsx.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`should render correctly 1`] = ` +exports[`should render correctly: azure 1`] = ` <Fragment> <header className="page-header" @@ -8,17 +8,17 @@ exports[`should render correctly 1`] = ` <h1 className="page-title" > - settings.pr_decoration.title + settings.almintegration.title </h1> </header> <div className="markdown small spacer-top big-spacer-bottom" > - settings.pr_decoration.description + settings.almintegration.description </div> <BoxedTabs onSelect={[MockFunction]} - selected="github" + selected="azure" tabs={ Array [ Object { @@ -44,6 +44,7 @@ exports[`should render correctly 1`] = ` /> Bitbucket Server </React.Fragment>, + "requiresBranchesEnabled": true, }, Object { "key": "azure", @@ -56,6 +57,7 @@ exports[`should render correctly 1`] = ` /> Azure DevOps Server </React.Fragment>, + "requiresBranchesEnabled": true, }, Object { "key": "gitlab", @@ -72,21 +74,17 @@ exports[`should render correctly 1`] = ` ] } /> - <div - className="boxed-group boxed-group-inner" - > - <GithubTab - definitions={Array []} - loading={true} - multipleAlmEnabled={false} - onDelete={[MockFunction]} - onUpdateDefinitions={[MockFunction]} - /> - </div> + <AzureTab + definitions={Array []} + loading={false} + multipleAlmEnabled={false} + onDelete={[MockFunction]} + onUpdateDefinitions={[MockFunction]} + /> </Fragment> `; -exports[`should render correctly 2`] = ` +exports[`should render correctly: bitbucket 1`] = ` <Fragment> <header className="page-header" @@ -94,17 +92,17 @@ exports[`should render correctly 2`] = ` <h1 className="page-title" > - settings.pr_decoration.title + settings.almintegration.title </h1> </header> <div className="markdown small spacer-top big-spacer-bottom" > - settings.pr_decoration.description + settings.almintegration.description </div> <BoxedTabs onSelect={[MockFunction]} - selected="github" + selected="bitbucket" tabs={ Array [ Object { @@ -130,6 +128,7 @@ exports[`should render correctly 2`] = ` /> Bitbucket Server </React.Fragment>, + "requiresBranchesEnabled": true, }, Object { "key": "azure", @@ -142,6 +141,7 @@ exports[`should render correctly 2`] = ` /> Azure DevOps Server </React.Fragment>, + "requiresBranchesEnabled": true, }, Object { "key": "gitlab", @@ -158,26 +158,17 @@ exports[`should render correctly 2`] = ` ] } /> - <div - className="boxed-group boxed-group-inner" - > - <GithubTab - definitions={Array []} - loading={false} - multipleAlmEnabled={false} - onDelete={[MockFunction]} - onUpdateDefinitions={[MockFunction]} - /> - </div> - <DeleteModal - id="keyToDelete" - onCancel={[MockFunction]} + <BitbucketTab + definitions={Array []} + loading={false} + multipleAlmEnabled={false} onDelete={[MockFunction]} + onUpdateDefinitions={[MockFunction]} /> </Fragment> `; -exports[`should render correctly 3`] = ` +exports[`should render correctly: default 1`] = ` <Fragment> <header className="page-header" @@ -185,17 +176,17 @@ exports[`should render correctly 3`] = ` <h1 className="page-title" > - settings.pr_decoration.title + settings.almintegration.title </h1> </header> <div className="markdown small spacer-top big-spacer-bottom" > - settings.pr_decoration.description + settings.almintegration.description </div> <BoxedTabs onSelect={[MockFunction]} - selected="azure" + selected="github" tabs={ Array [ Object { @@ -221,6 +212,7 @@ exports[`should render correctly 3`] = ` /> Bitbucket Server </React.Fragment>, + "requiresBranchesEnabled": true, }, Object { "key": "azure", @@ -233,6 +225,7 @@ exports[`should render correctly 3`] = ` /> Azure DevOps Server </React.Fragment>, + "requiresBranchesEnabled": true, }, Object { "key": "gitlab", @@ -249,21 +242,18 @@ exports[`should render correctly 3`] = ` ] } /> - <div - className="boxed-group boxed-group-inner" - > - <AzureTab - definitions={Array []} - loading={false} - multipleAlmEnabled={false} - onDelete={[MockFunction]} - onUpdateDefinitions={[MockFunction]} - /> - </div> + <GithubTab + branchesEnabled={true} + definitions={Array []} + loading={false} + multipleAlmEnabled={false} + onDelete={[MockFunction]} + onUpdateDefinitions={[MockFunction]} + /> </Fragment> `; -exports[`should render correctly 4`] = ` +exports[`should render correctly: delete modal 1`] = ` <Fragment> <header className="page-header" @@ -271,17 +261,17 @@ exports[`should render correctly 4`] = ` <h1 className="page-title" > - settings.pr_decoration.title + settings.almintegration.title </h1> </header> <div className="markdown small spacer-top big-spacer-bottom" > - settings.pr_decoration.description + settings.almintegration.description </div> <BoxedTabs onSelect={[MockFunction]} - selected="bitbucket" + selected="github" tabs={ Array [ Object { @@ -307,6 +297,7 @@ exports[`should render correctly 4`] = ` /> Bitbucket Server </React.Fragment>, + "requiresBranchesEnabled": true, }, Object { "key": "azure", @@ -319,6 +310,7 @@ exports[`should render correctly 4`] = ` /> Azure DevOps Server </React.Fragment>, + "requiresBranchesEnabled": true, }, Object { "key": "gitlab", @@ -335,21 +327,23 @@ exports[`should render correctly 4`] = ` ] } /> - <div - className="boxed-group boxed-group-inner" - > - <BitbucketTab - definitions={Array []} - loading={false} - multipleAlmEnabled={false} - onDelete={[MockFunction]} - onUpdateDefinitions={[MockFunction]} - /> - </div> + <GithubTab + branchesEnabled={true} + definitions={Array []} + loading={false} + multipleAlmEnabled={false} + onDelete={[MockFunction]} + onUpdateDefinitions={[MockFunction]} + /> + <DeleteModal + id="keyToDelete" + onCancel={[MockFunction]} + onDelete={[MockFunction]} + /> </Fragment> `; -exports[`should render correctly 5`] = ` +exports[`should render correctly: gitlab 1`] = ` <Fragment> <header className="page-header" @@ -357,13 +351,13 @@ exports[`should render correctly 5`] = ` <h1 className="page-title" > - settings.pr_decoration.title + settings.almintegration.title </h1> </header> <div className="markdown small spacer-top big-spacer-bottom" > - settings.pr_decoration.description + settings.almintegration.description </div> <BoxedTabs onSelect={[MockFunction]} @@ -393,6 +387,7 @@ exports[`should render correctly 5`] = ` /> Bitbucket Server </React.Fragment>, + "requiresBranchesEnabled": true, }, Object { "key": "azure", @@ -405,6 +400,7 @@ exports[`should render correctly 5`] = ` /> Azure DevOps Server </React.Fragment>, + "requiresBranchesEnabled": true, }, Object { "key": "gitlab", @@ -421,16 +417,98 @@ exports[`should render correctly 5`] = ` ] } /> + <GitlabTab + branchesEnabled={true} + definitions={Array []} + loading={false} + multipleAlmEnabled={false} + onDelete={[MockFunction]} + onUpdateDefinitions={[MockFunction]} + /> +</Fragment> +`; + +exports[`should render correctly: loading 1`] = ` +<Fragment> + <header + className="page-header" + > + <h1 + className="page-title" + > + settings.almintegration.title + </h1> + </header> <div - className="boxed-group boxed-group-inner" + className="markdown small spacer-top big-spacer-bottom" > - <GitlabTab - definitions={Array []} - loading={false} - multipleAlmEnabled={false} - onDelete={[MockFunction]} - onUpdateDefinitions={[MockFunction]} - /> + settings.almintegration.description </div> + <BoxedTabs + onSelect={[MockFunction]} + selected="github" + tabs={ + Array [ + Object { + "key": "github", + "label": <React.Fragment> + <img + alt="github" + className="spacer-right" + height={16} + src="/images/alm/github.svg" + /> + GitHub + </React.Fragment>, + }, + Object { + "key": "bitbucket", + "label": <React.Fragment> + <img + alt="bitbucket" + className="spacer-right" + height={16} + src="/images/alm/bitbucket.svg" + /> + Bitbucket Server + </React.Fragment>, + "requiresBranchesEnabled": true, + }, + Object { + "key": "azure", + "label": <React.Fragment> + <img + alt="azure" + className="spacer-right" + height={16} + src="/images/alm/azure.svg" + /> + Azure DevOps Server + </React.Fragment>, + "requiresBranchesEnabled": true, + }, + Object { + "key": "gitlab", + "label": <React.Fragment> + <img + alt="gitlab" + className="spacer-right" + height={16} + src="/images/alm/gitlab.svg" + /> + GitLab + </React.Fragment>, + }, + ] + } + /> + <GithubTab + branchesEnabled={true} + definitions={Array []} + loading={true} + multipleAlmEnabled={false} + onDelete={[MockFunction]} + onUpdateDefinitions={[MockFunction]} + /> </Fragment> `; diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmTab-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTab-test.tsx.snap index 5d3f8507f61..5d3f8507f61 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmTab-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTab-test.tsx.snap diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTabRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTabRenderer-test.tsx.snap new file mode 100644 index 00000000000..64f7ac5fa04 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AlmTabRenderer-test.tsx.snap @@ -0,0 +1,334 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly for multi-ALM binding: editing a definition 1`] = ` +<div + className="big-padded" +> + <DeferredSpinner + loading={false} + timeout={100} + > + <AlmBindingDefinitionsTable + additionalColumnsHeaders={ + Array [ + "url", + "app_id", + ] + } + alm="github" + definitions={ + Array [ + Object { + "additionalColumns": Array [ + "http://github.enterprise.com", + "123456", + ], + "key": "key", + }, + ] + } + onCreate={[MockFunction]} + onDelete={[MockFunction]} + onEdit={[MockFunction]} + /> + <AlmBindingDefinitionForm + bindingDefinition={ + Object { + "appId": "123456", + "key": "key", + "privateKey": "asdf1234", + "url": "http://github.enterprise.com", + } + } + help={ + <FormattedMessage + defaultMessage="settings.almintegration.github.info" + id="settings.almintegration.github.info" + values={ + Object { + "link": <Link + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to="/documentation/analysis/pr-decoration/" + > + learn_more + </Link>, + } + } + /> + } + onCancel={[MockFunction]} + onSubmit={[MockFunction]} + showInModal={true} + > + <Component /> + </AlmBindingDefinitionForm> + </DeferredSpinner> +</div> +`; + +exports[`should render correctly for multi-ALM binding: loaded 1`] = ` +<div + className="big-padded" +> + <DeferredSpinner + loading={false} + timeout={100} + > + <AlmBindingDefinitionsTable + additionalColumnsHeaders={ + Array [ + "url", + "app_id", + ] + } + alm="github" + definitions={ + Array [ + Object { + "additionalColumns": Array [ + "http://github.enterprise.com", + "123456", + ], + "key": "key", + }, + ] + } + onCreate={[MockFunction]} + onDelete={[MockFunction]} + onEdit={[MockFunction]} + /> + </DeferredSpinner> +</div> +`; + +exports[`should render correctly for multi-ALM binding: loading 1`] = ` +<div + className="big-padded" +> + <DeferredSpinner + loading={true} + timeout={100} + > + <AlmBindingDefinitionsTable + additionalColumnsHeaders={ + Array [ + "url", + "app_id", + ] + } + alm="github" + definitions={ + Array [ + Object { + "additionalColumns": Array [ + "http://github.enterprise.com", + "123456", + ], + "key": "key", + }, + ] + } + onCreate={[MockFunction]} + onDelete={[MockFunction]} + onEdit={[MockFunction]} + /> + </DeferredSpinner> +</div> +`; + +exports[`should render correctly for multi-ALM binding: with features 1`] = ` +<div + className="big-padded" +> + <DeferredSpinner + loading={false} + timeout={100} + > + <AlmBindingDefinitionsTable + additionalColumnsHeaders={ + Array [ + "url", + "app_id", + ] + } + alm="github" + definitions={ + Array [ + Object { + "additionalColumns": Array [ + "http://github.enterprise.com", + "123456", + ], + "key": "key", + }, + ] + } + onCreate={[MockFunction]} + onDelete={[MockFunction]} + onEdit={[MockFunction]} + /> + </DeferredSpinner> + <div + className="big-spacer-top big-padded-top bordered-top" + > + <h3 + className="big-spacer-bottom" + > + settings.almintegration.features + </h3> + <div + className="display-flex-wrap" + > + <AlmIntegrationFeatureBox + active={true} + description="Bar" + key="0" + name="Foo" + /> + <AlmIntegrationFeatureBox + active={false} + description="Bim" + key="1" + name="Baz" + /> + </div> + </div> +</div> +`; + +exports[`should render correctly for single-ALM binding 1`] = ` +<div + className="big-padded" +> + <AlmBindingDefinitionForm + bindingDefinition={ + Object { + "appId": "123456", + "key": "key", + "privateKey": "asdf1234", + "url": "http://github.enterprise.com", + } + } + help={ + <FormattedMessage + defaultMessage="settings.almintegration.github.info" + id="settings.almintegration.github.info" + values={ + Object { + "link": <Link + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to="/documentation/analysis/pr-decoration/" + > + learn_more + </Link>, + } + } + /> + } + hideKeyField={true} + loading={true} + onCancel={[MockFunction]} + onDelete={[MockFunction]} + onEdit={[MockFunction]} + onSubmit={[MockFunction]} + readOnly={true} + success={false} + > + <Component /> + </AlmBindingDefinitionForm> +</div> +`; + +exports[`should render correctly for single-ALM binding 2`] = ` +<div + className="big-padded" +> + <AlmBindingDefinitionForm + bindingDefinition={ + Object { + "appId": "123456", + "key": "key", + "privateKey": "asdf1234", + "url": "http://github.enterprise.com", + } + } + help={ + <FormattedMessage + defaultMessage="settings.almintegration.github.info" + id="settings.almintegration.github.info" + values={ + Object { + "link": <Link + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to="/documentation/analysis/pr-decoration/" + > + learn_more + </Link>, + } + } + /> + } + hideKeyField={true} + loading={false} + onCancel={[MockFunction]} + onDelete={[MockFunction]} + onEdit={[MockFunction]} + onSubmit={[MockFunction]} + readOnly={true} + success={false} + > + <Component /> + </AlmBindingDefinitionForm> +</div> +`; + +exports[`should render correctly for single-ALM binding 3`] = ` +<div + className="big-padded" +> + <AlmBindingDefinitionForm + bindingDefinition={ + Object { + "appId": "123456", + "key": "key", + "privateKey": "asdf1234", + "url": "http://github.enterprise.com", + } + } + help={ + <FormattedMessage + defaultMessage="settings.almintegration.github.info" + id="settings.almintegration.github.info" + values={ + Object { + "link": <Link + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to="/documentation/analysis/pr-decoration/" + > + learn_more + </Link>, + } + } + /> + } + hideKeyField={true} + loading={false} + onCancel={[MockFunction]} + onDelete={[MockFunction]} + onEdit={[MockFunction]} + onSubmit={[MockFunction]} + readOnly={true} + success={false} + > + <Component /> + </AlmBindingDefinitionForm> +</div> +`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AzureForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AzureForm-test.tsx.snap index b980d1d72cd..ba43fbcd654 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AzureForm-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AzureForm-test.tsx.snap @@ -2,16 +2,16 @@ exports[`should render correctly 1`] = ` <Fragment> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField autoFocus={true} - help="settings.pr_decoration.form.name.azure.help" + help="settings.almintegration.form.name.azure.help" id="name.azure" onFieldChange={[MockFunction]} propKey="key" value="" /> - <AlmDefinitionFormField - help="settings.pr_decoration.form.personal_access_token.azure.help" + <AlmBindingDefinitionFormField + help="settings.almintegration.form.personal_access_token.azure.help" id="personal_access_token" isTextArea={true} onFieldChange={[MockFunction]} @@ -23,16 +23,16 @@ exports[`should render correctly 1`] = ` exports[`should render correctly 2`] = ` <Fragment> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField autoFocus={true} - help="settings.pr_decoration.form.name.azure.help" + help="settings.almintegration.form.name.azure.help" id="name.azure" onFieldChange={[MockFunction]} propKey="key" value="key" /> - <AlmDefinitionFormField - help="settings.pr_decoration.form.personal_access_token.azure.help" + <AlmBindingDefinitionFormField + help="settings.almintegration.form.personal_access_token.azure.help" id="personal_access_token" isTextArea={true} onFieldChange={[MockFunction]} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AzureTab-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AzureTab-test.tsx.snap new file mode 100644 index 00000000000..b5eda86fe17 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/AzureTab-test.tsx.snap @@ -0,0 +1,42 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<div + className="bordered" +> + <AlmTab + alm="azure" + createConfiguration={[Function]} + defaultBinding={ + Object { + "key": "", + "personalAccessToken": "", + } + } + definitions={ + Array [ + Object { + "key": "key", + "personalAccessToken": "asdf1234", + }, + ] + } + features={ + Array [ + Object { + "active": true, + "description": "settings.almintegration.feature.pr_decoration.description", + "inactiveReason": "settings.almintegration.feature.need_at_least_1_binding", + "name": "settings.almintegration.feature.pr_decoration.title", + }, + ] + } + form={[Function]} + loading={false} + multipleAlmEnabled={true} + onDelete={[MockFunction]} + onUpdateDefinitions={[MockFunction]} + updateConfiguration={[Function]} + /> +</div> +`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/BitbucketForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketForm-test.tsx.snap index 831e13ea2e5..b936abc487c 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/BitbucketForm-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketForm-test.tsx.snap @@ -2,20 +2,20 @@ exports[`should render correctly 1`] = ` <Fragment> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField autoFocus={true} - help="settings.pr_decoration.form.name.bitbucket.help" + help="settings.almintegration.form.name.bitbucket.help" id="name.bitbucket" maxLength={100} onFieldChange={[MockFunction]} propKey="key" value="" /> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField help={ <FormattedMessage - defaultMessage="settings.pr_decoration.form.url.bitbucket.help" - id="settings.pr_decoration.form.url.bitbucket.help" + defaultMessage="settings.almintegration.form.url.bitbucket.help" + id="settings.almintegration.form.url.bitbucket.help" values={ Object { "example": "https://bitbucket-server.your-company.com", @@ -29,7 +29,7 @@ exports[`should render correctly 1`] = ` propKey="url" value="" /> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField id="personal_access_token" isTextArea={true} onFieldChange={[MockFunction]} @@ -41,20 +41,20 @@ exports[`should render correctly 1`] = ` exports[`should render correctly 2`] = ` <Fragment> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField autoFocus={true} - help="settings.pr_decoration.form.name.bitbucket.help" + help="settings.almintegration.form.name.bitbucket.help" id="name.bitbucket" maxLength={100} onFieldChange={[MockFunction]} propKey="key" value="key" /> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField help={ <FormattedMessage - defaultMessage="settings.pr_decoration.form.url.bitbucket.help" - id="settings.pr_decoration.form.url.bitbucket.help" + defaultMessage="settings.almintegration.form.url.bitbucket.help" + id="settings.almintegration.form.url.bitbucket.help" values={ Object { "example": "https://bitbucket-server.your-company.com", @@ -68,7 +68,7 @@ exports[`should render correctly 2`] = ` propKey="url" value="http://bbs.enterprise.com" /> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField id="personal_access_token" isTextArea={true} onFieldChange={[MockFunction]} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketTab-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketTab-test.tsx.snap new file mode 100644 index 00000000000..35279875066 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/BitbucketTab-test.tsx.snap @@ -0,0 +1,112 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly 1`] = ` +<div + className="bordered" +> + <AlmTab + additionalColumnsHeaders={ + Array [ + "settings.almintegration.table.column.bitbucket.url", + ] + } + additionalColumnsKeys={ + Array [ + "url", + ] + } + additionalTableInfo={ + <Alert + className="big-spacer-bottom width-50" + variant="info" + > + <FormattedMessage + defaultMessage="settings.almintegration.feature.alm_repo_import.disabled_if_multiple_bbs_instances" + id="settings.almintegration.feature.alm_repo_import.disabled_if_multiple_bbs_instances" + values={ + Object { + "feature": <em> + settings.almintegration.feature.alm_repo_import.title + </em>, + } + } + /> + </Alert> + } + alm="bitbucket" + createConfiguration={[Function]} + defaultBinding={ + Object { + "key": "", + "personalAccessToken": "", + "url": "", + } + } + definitions={ + Array [ + Object { + "key": "key", + "personalAccessToken": "asdf1234", + "url": "http://bbs.enterprise.com", + }, + ] + } + features={ + Array [ + Object { + "active": true, + "description": "settings.almintegration.feature.pr_decoration.description", + "inactiveReason": "settings.almintegration.feature.need_at_least_1_binding", + "name": "settings.almintegration.feature.pr_decoration.title", + }, + Object { + "active": true, + "description": "settings.almintegration.feature.alm_repo_import.description", + "inactiveReason": "onboarding.create_project.too_many_bbs_instances_X.1", + "name": "settings.almintegration.feature.alm_repo_import.title", + }, + ] + } + form={[Function]} + help={ + <React.Fragment> + <h3> + onboarding.create_project.pat_help.title + </h3> + <p + className="big-spacer-top" + > + settings.almintegration.bitbucket.help_1 + </p> + <ul + className="big-spacer-top list-styled" + > + <li> + settings.almintegration.bitbucket.help_2 + </li> + <li> + settings.almintegration.bitbucket.help_3 + </li> + </ul> + <p + className="big-spacer-top big-spacer-bottom" + > + <Link + onlyActiveOnIndex={false} + style={Object {}} + target="_blank" + to="/documentation/analysis/pr-decoration/" + > + learn_more + </Link> + </p> + </React.Fragment> + } + loading={false} + multipleAlmEnabled={true} + onDelete={[MockFunction]} + onUpdateDefinitions={[MockFunction]} + updateConfiguration={[Function]} + /> +</div> +`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/DeleteModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/DeleteModal-test.tsx.snap index 710915a2ceb..78660781f84 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/DeleteModal-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/DeleteModal-test.tsx.snap @@ -4,7 +4,7 @@ exports[`should render correctly 1`] = ` <ConfirmModal confirmButtonText="delete" confirmData="1" - header="settings.pr_decoration.delete.header" + header="settings.almintegration.delete.header" isDestructive={true} onClose={[MockFunction]} onConfirm={[MockFunction]} @@ -13,8 +13,8 @@ exports[`should render correctly 1`] = ` className="spacer-bottom" > <FormattedMessage - defaultMessage="settings.pr_decoration.delete.message" - id="settings.pr_decoration.delete.message" + defaultMessage="settings.almintegration.delete.message" + id="settings.almintegration.delete.message" values={ Object { "id": <b> @@ -25,7 +25,7 @@ exports[`should render correctly 1`] = ` /> </p> <p> - settings.pr_decoration.delete.info.4 + settings.almintegration.delete.info.4 </p> </ConfirmModal> @@ -35,7 +35,7 @@ exports[`should render correctly 2`] = ` <ConfirmModal confirmButtonText="delete" confirmData="1" - header="settings.pr_decoration.delete.header" + header="settings.almintegration.delete.header" isDestructive={true} onClose={[MockFunction]} onConfirm={[MockFunction]} @@ -44,8 +44,8 @@ exports[`should render correctly 2`] = ` className="spacer-bottom" > <FormattedMessage - defaultMessage="settings.pr_decoration.delete.message" - id="settings.pr_decoration.delete.message" + defaultMessage="settings.almintegration.delete.message" + id="settings.almintegration.delete.message" values={ Object { "id": <b> @@ -56,7 +56,7 @@ exports[`should render correctly 2`] = ` /> </p> <p> - settings.pr_decoration.delete.no_info + settings.almintegration.delete.no_info </p> </ConfirmModal> `; diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GithubForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GithubForm-test.tsx.snap index 3f4492a255d..4015c170435 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GithubForm-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GithubForm-test.tsx.snap @@ -2,25 +2,25 @@ exports[`should render correctly 1`] = ` <Fragment> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField autoFocus={true} - help="settings.pr_decoration.form.name.github.help" + help="settings.almintegration.form.name.github.help" id="name.github" onFieldChange={[MockFunction]} propKey="key" value="" /> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField help={ <React.Fragment> - settings.pr_decoration.form.url.github.help1 + settings.almintegration.form.url.github.help1 <br /> <em> https://github.company.com/api/v3 </em> <br /> <br /> - settings.pr_decoration.form.url.github.help2 + settings.almintegration.form.url.github.help2 <br /> <em> https://api.github.com/ @@ -33,14 +33,14 @@ exports[`should render correctly 1`] = ` propKey="url" value="" /> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField id="app_id" maxLength={80} onFieldChange={[MockFunction]} propKey="appId" value="" /> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField id="private_key" isTextArea={true} onFieldChange={[MockFunction]} @@ -52,25 +52,25 @@ exports[`should render correctly 1`] = ` exports[`should render correctly 2`] = ` <Fragment> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField autoFocus={true} - help="settings.pr_decoration.form.name.github.help" + help="settings.almintegration.form.name.github.help" id="name.github" onFieldChange={[MockFunction]} propKey="key" value="key" /> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField help={ <React.Fragment> - settings.pr_decoration.form.url.github.help1 + settings.almintegration.form.url.github.help1 <br /> <em> https://github.company.com/api/v3 </em> <br /> <br /> - settings.pr_decoration.form.url.github.help2 + settings.almintegration.form.url.github.help2 <br /> <em> https://api.github.com/ @@ -83,14 +83,14 @@ exports[`should render correctly 2`] = ` propKey="url" value="http://github.enterprise.com" /> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField id="app_id" maxLength={80} onFieldChange={[MockFunction]} propKey="appId" value="123456" /> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField id="private_key" isTextArea={true} onFieldChange={[MockFunction]} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GithubTab-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GithubTab-test.tsx.snap new file mode 100644 index 00000000000..1f1420d5604 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GithubTab-test.tsx.snap @@ -0,0 +1,84 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly: with branch support 1`] = ` +<div + className="bordered" +> + <AlmTab + additionalColumnsHeaders={ + Array [ + "settings.almintegration.table.column.github.url", + "settings.almintegration.table.column.app_id", + ] + } + additionalColumnsKeys={ + Array [ + "appId", + "url", + ] + } + alm="github" + createConfiguration={[Function]} + defaultBinding={ + Object { + "appId": "", + "key": "", + "privateKey": "", + "url": "", + } + } + definitions={ + Array [ + Object { + "appId": "123456", + "key": "key", + "privateKey": "asdf1234", + "url": "http://github.enterprise.com", + }, + ] + } + features={ + Array [ + Object { + "active": true, + "description": "settings.almintegration.feature.pr_decoration.description", + "inactiveReason": "settings.almintegration.feature.need_at_least_1_binding", + "name": "settings.almintegration.feature.pr_decoration.title", + }, + ] + } + form={[Function]} + loading={false} + multipleAlmEnabled={true} + onDelete={[MockFunction]} + onUpdateDefinitions={[MockFunction]} + updateConfiguration={[Function]} + /> + <div + className="huge-spacer-top huge-spacer-bottom bordered-top" + /> + <div + className="big-padded" + > + <Connect(SubCategoryDefinitionsList) + category="almintegration" + subCategory="github" + /> + </div> +</div> +`; + +exports[`should render correctly: without branch support 1`] = ` +<div + className="bordered" +> + <div + className="big-padded" + > + <Connect(SubCategoryDefinitionsList) + category="almintegration" + subCategory="github" + /> + </div> +</div> +`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GitlabForm-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GitlabForm-test.tsx.snap index 8c2b54c3887..ff9f07e6dc1 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GitlabForm-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GitlabForm-test.tsx.snap @@ -2,16 +2,16 @@ exports[`should render correctly 1`] = ` <Fragment> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField autoFocus={true} - help="settings.pr_decoration.form.name.gitlab.help" + help="settings.almintegration.form.name.gitlab.help" id="name.gitlab" onFieldChange={[MockFunction]} propKey="key" value="" /> - <AlmDefinitionFormField - help="settings.pr_decoration.form.personal_access_token.gitlab.help" + <AlmBindingDefinitionFormField + help="settings.almintegration.form.personal_access_token.gitlab.help" id="personal_access_token" isTextArea={true} onFieldChange={[MockFunction]} @@ -23,16 +23,16 @@ exports[`should render correctly 1`] = ` exports[`should render correctly 2`] = ` <Fragment> - <AlmDefinitionFormField + <AlmBindingDefinitionFormField autoFocus={true} - help="settings.pr_decoration.form.name.gitlab.help" + help="settings.almintegration.form.name.gitlab.help" id="name.gitlab" onFieldChange={[MockFunction]} propKey="key" value="foo" /> - <AlmDefinitionFormField - help="settings.pr_decoration.form.personal_access_token.gitlab.help" + <AlmBindingDefinitionFormField + help="settings.almintegration.form.personal_access_token.gitlab.help" id="personal_access_token" isTextArea={true} onFieldChange={[MockFunction]} diff --git a/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GitlabTab-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GitlabTab-test.tsx.snap new file mode 100644 index 00000000000..46930b7d06c --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/almIntegration/__tests__/__snapshots__/GitlabTab-test.tsx.snap @@ -0,0 +1,68 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`should render correctly: with branch support 1`] = ` +<div + className="bordered" +> + <AlmTab + alm="gitlab" + createConfiguration={[Function]} + defaultBinding={ + Object { + "key": "", + "personalAccessToken": "", + } + } + definitions={ + Array [ + Object { + "key": "foo", + "personalAccessToken": "foobar", + }, + ] + } + features={ + Array [ + Object { + "active": true, + "description": "settings.almintegration.feature.mr_decoration.description", + "inactiveReason": "settings.almintegration.feature.need_at_least_1_binding", + "name": "settings.almintegration.feature.mr_decoration.title", + }, + ] + } + form={[Function]} + loading={false} + multipleAlmEnabled={true} + onDelete={[MockFunction]} + onUpdateDefinitions={[MockFunction]} + updateConfiguration={[Function]} + /> + <div + className="huge-spacer-top huge-spacer-bottom bordered-top" + /> + <div + className="big-padded" + > + <Connect(SubCategoryDefinitionsList) + category="almintegration" + subCategory="gitlab" + /> + </div> +</div> +`; + +exports[`should render correctly: without branch support 1`] = ` +<div + className="bordered" +> + <div + className="big-padded" + > + <Connect(SubCategoryDefinitionsList) + category="almintegration" + subCategory="gitlab" + /> + </div> +</div> +`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmTabRenderer.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmTabRenderer.tsx deleted file mode 100644 index 3daa7b0ce03..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmTabRenderer.tsx +++ /dev/null @@ -1,136 +0,0 @@ -/* - * 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'; -import { FormattedMessage } from 'react-intl'; -import { Link } from 'react-router'; -import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import { AlmSettingsBinding, ALM_KEYS } from '../../../../types/alm-settings'; -import AlmPRDecorationForm, { AlmPRDecorationFormChildrenProps } from './AlmPRDecorationForm'; -import AlmPRDecorationTable from './AlmPRDecorationTable'; - -export interface AlmTabRendererProps<B> { - additionalColumnsHeaders: string[]; - additionalColumnsKeys: Array<keyof B>; - alm: ALM_KEYS; - editedDefinition?: B; - defaultBinding: B; - definitions: B[]; - form: (props: AlmPRDecorationFormChildrenProps<B>) => React.ReactNode; - loading: boolean; - multipleAlmEnabled: boolean; - onCancel: () => void; - onCreate: () => void; - onDelete: (definitionKey: string) => void; - onEdit: (definitionKey: string) => void; - onSubmit: (config: B, originalKey: string) => void; - success: boolean; -} - -export default function AlmTabRenderer<B extends AlmSettingsBinding>( - props: AlmTabRendererProps<B> -) { - const { - additionalColumnsHeaders, - additionalColumnsKeys, - alm, - defaultBinding, - definitions, - editedDefinition, - form, - loading, - multipleAlmEnabled, - success - } = props; - - let definition: B | undefined; - let mappedDefinitions: Array<{ key: string; additionalColumns: string[] }> = []; - let showEdit: boolean | undefined; - - if (!multipleAlmEnabled) { - definition = editedDefinition; - if (definition === undefined && definitions.length > 0) { - definition = definitions[0]; - } - showEdit = definition && editedDefinition === undefined; - } else { - mappedDefinitions = definitions.map(({ key, ...properties }) => { - const additionalColumns = additionalColumnsKeys.map(k => (properties as any)[k]); - return { - key, - additionalColumns - }; - }); - } - - const help = ( - <FormattedMessage - defaultMessage={translate(`settings.pr_decoration.${alm}.info`)} - id={`settings.pr_decoration.${alm}.info`} - values={{ - link: ( - <Link target="_blank" to="/documentation/analysis/pr-decoration/"> - {translate('learn_more')} - </Link> - ) - }} - /> - ); - - return multipleAlmEnabled ? ( - <DeferredSpinner loading={loading}> - <AlmPRDecorationTable - additionalColumnsHeaders={additionalColumnsHeaders} - alm={alm} - definitions={mappedDefinitions} - onCreate={props.onCreate} - onDelete={props.onDelete} - onEdit={props.onEdit} - /> - - {editedDefinition && ( - <AlmPRDecorationForm - alm={alm} - bindingDefinition={editedDefinition} - help={help} - onCancel={props.onCancel} - onSubmit={props.onSubmit} - showInModal={true}> - {form} - </AlmPRDecorationForm> - )} - </DeferredSpinner> - ) : ( - <AlmPRDecorationForm - alm={alm} - bindingDefinition={definition || defaultBinding} - help={help} - hideKeyField={true} - loading={loading} - onCancel={props.onCancel} - onDelete={definition ? props.onDelete : undefined} - onEdit={showEdit ? props.onEdit : undefined} - onSubmit={props.onSubmit} - readOnly={showEdit} - success={success}> - {form} - </AlmPRDecorationForm> - ); -} diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/BitbucketTab.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/BitbucketTab.tsx deleted file mode 100644 index 9de99d62378..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/BitbucketTab.tsx +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import { - createBitbucketConfiguration, - updateBitbucketConfiguration -} from '../../../../api/almSettings'; -import { ALM_KEYS, BitbucketBindingDefinition } from '../../../../types/alm-settings'; -import AlmTab from './AlmTab'; -import BitbucketForm from './BitbucketForm'; - -export interface BitbucketTabProps { - definitions: BitbucketBindingDefinition[]; - loading: boolean; - multipleAlmEnabled: boolean; - onDelete: (definitionKey: string) => void; - onUpdateDefinitions: () => void; -} - -export default function BitbucketTab(props: BitbucketTabProps) { - const { multipleAlmEnabled, definitions, loading } = props; - - return ( - <AlmTab - additionalColumnsHeaders={[translate('settings.pr_decoration.table.column.bitbucket.url')]} - additionalColumnsKeys={['url']} - alm={ALM_KEYS.BITBUCKET} - createConfiguration={createBitbucketConfiguration} - defaultBinding={{ key: '', url: '', personalAccessToken: '' }} - definitions={definitions} - form={childProps => <BitbucketForm {...childProps} />} - loading={loading} - multipleAlmEnabled={multipleAlmEnabled} - onDelete={props.onDelete} - onUpdateDefinitions={props.onUpdateDefinitions} - updateConfiguration={updateBitbucketConfiguration} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GithubTab.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GithubTab.tsx deleted file mode 100644 index 5c10574fa24..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GithubTab.tsx +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import { createGithubConfiguration, updateGithubConfiguration } from '../../../../api/almSettings'; -import { ALM_KEYS, GithubBindingDefinition } from '../../../../types/alm-settings'; -import AlmTab from './AlmTab'; -import GithubForm from './GithubForm'; - -export interface GithubTabProps { - definitions: GithubBindingDefinition[]; - loading: boolean; - multipleAlmEnabled: boolean; - onDelete: (definitionKey: string) => void; - onUpdateDefinitions: () => void; -} - -export default function GithubTab(props: GithubTabProps) { - const { multipleAlmEnabled, definitions, loading } = props; - - return ( - <AlmTab - additionalColumnsHeaders={[ - translate('settings.pr_decoration.table.column.github.url'), - translate('settings.pr_decoration.table.column.app_id') - ]} - additionalColumnsKeys={['appId', 'url']} - alm={ALM_KEYS.GITHUB} - createConfiguration={createGithubConfiguration} - defaultBinding={{ key: '', appId: '', url: '', privateKey: '' }} - definitions={definitions} - form={childProps => <GithubForm {...childProps} />} - loading={loading} - multipleAlmEnabled={multipleAlmEnabled} - onDelete={props.onDelete} - onUpdateDefinitions={props.onUpdateDefinitions} - updateConfiguration={updateGithubConfiguration} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GitlabTab.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GitlabTab.tsx deleted file mode 100644 index 145694a6247..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GitlabTab.tsx +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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'; -import { createGitlabConfiguration, updateGitlabConfiguration } from '../../../../api/almSettings'; -import { ALM_KEYS, GitlabBindingDefinition } from '../../../../types/alm-settings'; -import AlmTab from './AlmTab'; -import GitlabForm from './GitlabForm'; - -export interface GitlabTabProps { - definitions: GitlabBindingDefinition[]; - loading: boolean; - multipleAlmEnabled: boolean; - onDelete: (definitionKey: string) => void; - onUpdateDefinitions: () => void; -} - -export default function GitlabTab(props: GitlabTabProps) { - const { multipleAlmEnabled, definitions, loading } = props; - - return ( - <AlmTab - alm={ALM_KEYS.GITLAB} - createConfiguration={createGitlabConfiguration} - defaultBinding={{ key: '', personalAccessToken: '' }} - definitions={definitions} - form={childProps => <GitlabForm {...childProps} />} - loading={loading} - multipleAlmEnabled={multipleAlmEnabled} - onDelete={props.onDelete} - onUpdateDefinitions={props.onUpdateDefinitions} - updateConfiguration={updateGitlabConfiguration} - /> - ); -} diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/PRDecorationTabs.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/PRDecorationTabs.tsx deleted file mode 100644 index e55c90e70b9..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/PRDecorationTabs.tsx +++ /dev/null @@ -1,186 +0,0 @@ -/* - * 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'; -import BoxedTabs from 'sonar-ui-common/components/controls/BoxedTabs'; -import { translate } from 'sonar-ui-common/helpers/l10n'; -import { getBaseUrl } from 'sonar-ui-common/helpers/urls'; -import { withAppState } from '../../../../components/hoc/withAppState'; -import { AlmSettingsBindingDefinitions, ALM_KEYS } from '../../../../types/alm-settings'; -import AzureTab from './AzureTab'; -import BitbucketTab from './BitbucketTab'; -import DeleteModal from './DeleteModal'; -import GithubTab from './GithubTab'; -import GitlabTab from './GitlabTab'; - -export interface PRDecorationTabsProps { - appState: Pick<T.AppState, 'multipleAlmEnabled'>; - currentAlm: ALM_KEYS; - definitionKeyForDeletion?: string; - definitions: AlmSettingsBindingDefinitions; - loading: boolean; - onCancel: () => void; - onConfirmDelete: (definitionKey: string) => void; - onDelete: (definitionKey: string) => void; - onSelectAlm: (alm: ALM_KEYS) => void; - onUpdateDefinitions: () => void; - projectCount?: number; -} - -export const almName = { - [ALM_KEYS.AZURE]: 'Azure DevOps Server', - [ALM_KEYS.BITBUCKET]: 'Bitbucket Server', - [ALM_KEYS.GITHUB]: 'GitHub', - [ALM_KEYS.GITLAB]: 'GitLab' -}; - -export function PRDecorationTabs(props: PRDecorationTabsProps) { - const { - appState: { multipleAlmEnabled }, - definitionKeyForDeletion, - definitions, - currentAlm, - loading, - projectCount - } = props; - - return ( - <> - <header className="page-header"> - <h1 className="page-title">{translate('settings.pr_decoration.title')}</h1> - </header> - - <div className="markdown small spacer-top big-spacer-bottom"> - {translate('settings.pr_decoration.description')} - </div> - <BoxedTabs - onSelect={props.onSelectAlm} - selected={currentAlm} - tabs={[ - { - key: ALM_KEYS.GITHUB, - label: ( - <> - <img - alt="github" - className="spacer-right" - height={16} - src={`${getBaseUrl()}/images/alm/github.svg`} - /> - {almName[ALM_KEYS.GITHUB]} - </> - ) - }, - { - key: ALM_KEYS.BITBUCKET, - label: ( - <> - <img - alt="bitbucket" - className="spacer-right" - height={16} - src={`${getBaseUrl()}/images/alm/bitbucket.svg`} - /> - {almName[ALM_KEYS.BITBUCKET]} - </> - ) - }, - { - key: ALM_KEYS.AZURE, - label: ( - <> - <img - alt="azure" - className="spacer-right" - height={16} - src={`${getBaseUrl()}/images/alm/azure.svg`} - /> - {almName[ALM_KEYS.AZURE]} - </> - ) - }, - { - key: ALM_KEYS.GITLAB, - label: ( - <> - <img - alt="gitlab" - className="spacer-right" - height={16} - src={`${getBaseUrl()}/images/alm/gitlab.svg`} - /> - {almName[ALM_KEYS.GITLAB]} - </> - ) - } - ]} - /> - - <div className="boxed-group boxed-group-inner"> - {currentAlm === ALM_KEYS.AZURE && ( - <AzureTab - definitions={definitions.azure} - loading={loading} - multipleAlmEnabled={Boolean(multipleAlmEnabled)} - onDelete={props.onDelete} - onUpdateDefinitions={props.onUpdateDefinitions} - /> - )} - {currentAlm === ALM_KEYS.BITBUCKET && ( - <BitbucketTab - definitions={definitions.bitbucket} - loading={loading} - multipleAlmEnabled={Boolean(multipleAlmEnabled)} - onDelete={props.onDelete} - onUpdateDefinitions={props.onUpdateDefinitions} - /> - )} - {currentAlm === ALM_KEYS.GITHUB && ( - <GithubTab - definitions={definitions.github} - loading={loading} - multipleAlmEnabled={Boolean(multipleAlmEnabled)} - onDelete={props.onDelete} - onUpdateDefinitions={props.onUpdateDefinitions} - /> - )} - {currentAlm === ALM_KEYS.GITLAB && ( - <GitlabTab - definitions={definitions.gitlab} - loading={loading} - multipleAlmEnabled={Boolean(multipleAlmEnabled)} - onDelete={props.onDelete} - onUpdateDefinitions={props.onUpdateDefinitions} - /> - )} - </div> - - {definitionKeyForDeletion && ( - <DeleteModal - id={definitionKeyForDeletion} - onCancel={props.onCancel} - onDelete={props.onConfirmDelete} - projectCount={projectCount} - /> - )} - </> - ); -} - -export default withAppState(PRDecorationTabs); diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmPRDecorationFormRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmPRDecorationFormRenderer-test.tsx.snap deleted file mode 100644 index fddedfb39c2..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmPRDecorationFormRenderer-test.tsx.snap +++ /dev/null @@ -1,121 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<form - className="views-form" - data-test="settings__alm-form" - onSubmit={[Function]} -> - <Component /> - <div - className="display-flex-center" - > - <SubmitButton - disabled={true} - > - settings.pr_decoration.form.save - </SubmitButton> - </div> -</form> -`; - -exports[`should render correctly 2`] = ` -<form - className="views-form" - data-test="settings__alm-form" - onSubmit={[Function]} -> - <Component /> - <div - className="display-flex-center" - > - <SubmitButton - disabled={true} - > - settings.pr_decoration.form.save - </SubmitButton> - <ResetButtonLink - className="spacer-left" - onClick={[MockFunction]} - > - cancel - </ResetButtonLink> - </div> -</form> -`; - -exports[`should render correctly 3`] = ` -<form - className="views-form" - data-test="settings__alm-form" - onSubmit={[Function]} -> - <Component /> - <div - className="display-flex-center" - > - <SubmitButton - disabled={true} - > - settings.pr_decoration.form.save - </SubmitButton> - <Button - className="button-red spacer-left" - disabled={false} - onClick={[MockFunction]} - > - delete - </Button> - </div> -</form> -`; - -exports[`should render correctly 4`] = ` -<form - className="views-form" - data-test="settings__alm-form" - onSubmit={[Function]} -> - <Component /> - <div - className="display-flex-center" - > - <SubmitButton - disabled={true} - > - settings.pr_decoration.form.save - </SubmitButton> - <span - className="text-success spacer-left" - > - <AlertSuccessIcon - className="spacer-right" - /> - settings.state.saved - </span> - </div> -</form> -`; - -exports[`should render correctly 5`] = ` -<form - className="views-form" - data-test="settings__alm-form" - onSubmit={[Function]} -> - <Component /> - <div - className="display-flex-center" - > - <SubmitButton - disabled={true} - > - settings.pr_decoration.form.save - </SubmitButton> - <DeferredSpinner - className="spacer-left" - timeout={100} - /> - </div> -</form> -`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmTabRenderer-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmTabRenderer-test.tsx.snap deleted file mode 100644 index 620640396af..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmTabRenderer-test.tsx.snap +++ /dev/null @@ -1,254 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly for multi-ALM binding: editing a definition 1`] = ` -<DeferredSpinner - loading={false} - timeout={100} -> - <AlmPRDecorationTable - additionalColumnsHeaders={ - Array [ - "url", - "app_id", - ] - } - alm="github" - definitions={ - Array [ - Object { - "additionalColumns": Array [ - "http://github.enterprise.com", - "123456", - ], - "key": "key", - }, - ] - } - onCreate={[MockFunction]} - onDelete={[MockFunction]} - onEdit={[MockFunction]} - /> - <AlmPRDecorationForm - alm="github" - bindingDefinition={ - Object { - "appId": "123456", - "key": "key", - "privateKey": "asdf1234", - "url": "http://github.enterprise.com", - } - } - help={ - <FormattedMessage - defaultMessage="settings.pr_decoration.github.info" - id="settings.pr_decoration.github.info" - values={ - Object { - "link": <Link - onlyActiveOnIndex={false} - style={Object {}} - target="_blank" - to="/documentation/analysis/pr-decoration/" - > - learn_more - </Link>, - } - } - /> - } - onCancel={[MockFunction]} - onSubmit={[MockFunction]} - showInModal={true} - > - <Component /> - </AlmPRDecorationForm> -</DeferredSpinner> -`; - -exports[`should render correctly for multi-ALM binding: loaded 1`] = ` -<DeferredSpinner - loading={false} - timeout={100} -> - <AlmPRDecorationTable - additionalColumnsHeaders={ - Array [ - "url", - "app_id", - ] - } - alm="github" - definitions={ - Array [ - Object { - "additionalColumns": Array [ - "http://github.enterprise.com", - "123456", - ], - "key": "key", - }, - ] - } - onCreate={[MockFunction]} - onDelete={[MockFunction]} - onEdit={[MockFunction]} - /> -</DeferredSpinner> -`; - -exports[`should render correctly for multi-ALM binding: loading 1`] = ` -<DeferredSpinner - loading={true} - timeout={100} -> - <AlmPRDecorationTable - additionalColumnsHeaders={ - Array [ - "url", - "app_id", - ] - } - alm="github" - definitions={ - Array [ - Object { - "additionalColumns": Array [ - "http://github.enterprise.com", - "123456", - ], - "key": "key", - }, - ] - } - onCreate={[MockFunction]} - onDelete={[MockFunction]} - onEdit={[MockFunction]} - /> -</DeferredSpinner> -`; - -exports[`should render correctly for single-ALM binding 1`] = ` -<AlmPRDecorationForm - alm="github" - bindingDefinition={ - Object { - "appId": "123456", - "key": "key", - "privateKey": "asdf1234", - "url": "http://github.enterprise.com", - } - } - help={ - <FormattedMessage - defaultMessage="settings.pr_decoration.github.info" - id="settings.pr_decoration.github.info" - values={ - Object { - "link": <Link - onlyActiveOnIndex={false} - style={Object {}} - target="_blank" - to="/documentation/analysis/pr-decoration/" - > - learn_more - </Link>, - } - } - /> - } - hideKeyField={true} - loading={true} - onCancel={[MockFunction]} - onDelete={[MockFunction]} - onEdit={[MockFunction]} - onSubmit={[MockFunction]} - readOnly={true} - success={false} -> - <Component /> -</AlmPRDecorationForm> -`; - -exports[`should render correctly for single-ALM binding 2`] = ` -<AlmPRDecorationForm - alm="github" - bindingDefinition={ - Object { - "appId": "123456", - "key": "key", - "privateKey": "asdf1234", - "url": "http://github.enterprise.com", - } - } - help={ - <FormattedMessage - defaultMessage="settings.pr_decoration.github.info" - id="settings.pr_decoration.github.info" - values={ - Object { - "link": <Link - onlyActiveOnIndex={false} - style={Object {}} - target="_blank" - to="/documentation/analysis/pr-decoration/" - > - learn_more - </Link>, - } - } - /> - } - hideKeyField={true} - loading={false} - onCancel={[MockFunction]} - onDelete={[MockFunction]} - onEdit={[MockFunction]} - onSubmit={[MockFunction]} - readOnly={true} - success={false} -> - <Component /> -</AlmPRDecorationForm> -`; - -exports[`should render correctly for single-ALM binding 3`] = ` -<AlmPRDecorationForm - alm="github" - bindingDefinition={ - Object { - "appId": "123456", - "key": "key", - "privateKey": "asdf1234", - "url": "http://github.enterprise.com", - } - } - help={ - <FormattedMessage - defaultMessage="settings.pr_decoration.github.info" - id="settings.pr_decoration.github.info" - values={ - Object { - "link": <Link - onlyActiveOnIndex={false} - style={Object {}} - target="_blank" - to="/documentation/analysis/pr-decoration/" - > - learn_more - </Link>, - } - } - /> - } - hideKeyField={true} - loading={false} - onCancel={[MockFunction]} - onDelete={[MockFunction]} - onEdit={[MockFunction]} - onSubmit={[MockFunction]} - readOnly={true} - success={false} -> - <Component /> -</AlmPRDecorationForm> -`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AzureTab-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AzureTab-test.tsx.snap deleted file mode 100644 index f73bb4670fe..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AzureTab-test.tsx.snap +++ /dev/null @@ -1,28 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<AlmTab - alm="azure" - createConfiguration={[Function]} - defaultBinding={ - Object { - "key": "", - "personalAccessToken": "", - } - } - definitions={ - Array [ - Object { - "key": "key", - "personalAccessToken": "asdf1234", - }, - ] - } - form={[Function]} - loading={false} - multipleAlmEnabled={true} - onDelete={[MockFunction]} - onUpdateDefinitions={[MockFunction]} - updateConfiguration={[Function]} -/> -`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/BitbucketTab-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/BitbucketTab-test.tsx.snap deleted file mode 100644 index 9d519a62456..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/BitbucketTab-test.tsx.snap +++ /dev/null @@ -1,40 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<AlmTab - additionalColumnsHeaders={ - Array [ - "settings.pr_decoration.table.column.bitbucket.url", - ] - } - additionalColumnsKeys={ - Array [ - "url", - ] - } - alm="bitbucket" - createConfiguration={[Function]} - defaultBinding={ - Object { - "key": "", - "personalAccessToken": "", - "url": "", - } - } - definitions={ - Array [ - Object { - "key": "key", - "personalAccessToken": "asdf1234", - "url": "http://bbs.enterprise.com", - }, - ] - } - form={[Function]} - loading={false} - multipleAlmEnabled={true} - onDelete={[MockFunction]} - onUpdateDefinitions={[MockFunction]} - updateConfiguration={[Function]} -/> -`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GithubTab-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GithubTab-test.tsx.snap deleted file mode 100644 index 2fc099dd893..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GithubTab-test.tsx.snap +++ /dev/null @@ -1,44 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<AlmTab - additionalColumnsHeaders={ - Array [ - "settings.pr_decoration.table.column.github.url", - "settings.pr_decoration.table.column.app_id", - ] - } - additionalColumnsKeys={ - Array [ - "appId", - "url", - ] - } - alm="github" - createConfiguration={[Function]} - defaultBinding={ - Object { - "appId": "", - "key": "", - "privateKey": "", - "url": "", - } - } - definitions={ - Array [ - Object { - "appId": "123456", - "key": "key", - "privateKey": "asdf1234", - "url": "http://github.enterprise.com", - }, - ] - } - form={[Function]} - loading={false} - multipleAlmEnabled={true} - onDelete={[MockFunction]} - onUpdateDefinitions={[MockFunction]} - updateConfiguration={[Function]} -/> -`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GitlabTab-test.tsx.snap b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GitlabTab-test.tsx.snap deleted file mode 100644 index fdee120ed9f..00000000000 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GitlabTab-test.tsx.snap +++ /dev/null @@ -1,28 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`should render correctly 1`] = ` -<AlmTab - alm="gitlab" - createConfiguration={[Function]} - defaultBinding={ - Object { - "key": "", - "personalAccessToken": "", - } - } - definitions={ - Array [ - Object { - "key": "foo", - "personalAccessToken": "foobar", - }, - ] - } - form={[Function]} - loading={false} - multipleAlmEnabled={true} - onDelete={[MockFunction]} - onUpdateDefinitions={[MockFunction]} - updateConfiguration={[Function]} -/> -`; diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBinding.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBinding.tsx index 1b1a3914dc1..4fa49ed870c 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBinding.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBinding.tsx @@ -26,9 +26,9 @@ import { setProjectBitbucketBinding, setProjectGithubBinding, setProjectGitlabBinding -} from '../../../../api/almSettings'; +} from '../../../../api/alm-settings'; import throwGlobalError from '../../../../app/utils/throwGlobalError'; -import { AlmSettingsInstance, ALM_KEYS, ProjectAlmBinding } from '../../../../types/alm-settings'; +import { AlmKeys, AlmSettingsInstance, ProjectAlmBinding } from '../../../../types/alm-settings'; import PRDecorationBindingRenderer from './PRDecorationBindingRenderer'; interface Props { @@ -45,11 +45,11 @@ interface State { success: boolean; } -const FIELDS_BY_ALM: { [almKey in ALM_KEYS]: Array<keyof T.Omit<ProjectAlmBinding, 'key'>> } = { - [ALM_KEYS.AZURE]: [], - [ALM_KEYS.BITBUCKET]: ['repository', 'slug'], - [ALM_KEYS.GITHUB]: ['repository'], - [ALM_KEYS.GITLAB]: [] +const FIELDS_BY_ALM: { [almKey in AlmKeys]: Array<keyof T.Omit<ProjectAlmBinding, 'key'>> } = { + [AlmKeys.Azure]: [], + [AlmKeys.Bitbucket]: ['repository', 'slug'], + [AlmKeys.GitHub]: ['repository'], + [AlmKeys.GitLab]: [] }; export default class PRDecorationBinding extends React.PureComponent<Props, State> { @@ -133,7 +133,7 @@ export default class PRDecorationBinding extends React.PureComponent<Props, Stat }; submitProjectAlmBinding( - alm: ALM_KEYS, + alm: AlmKeys, key: string, almSpecificFields?: T.Omit<ProjectAlmBinding, 'key'> ): Promise<void> { @@ -141,12 +141,12 @@ export default class PRDecorationBinding extends React.PureComponent<Props, Stat const project = this.props.component.key; switch (alm) { - case ALM_KEYS.AZURE: + case AlmKeys.Azure: return setProjectAzureBinding({ almSetting, project }); - case ALM_KEYS.BITBUCKET: { + case AlmKeys.Bitbucket: { if (!almSpecificFields) { return Promise.reject(); } @@ -158,7 +158,7 @@ export default class PRDecorationBinding extends React.PureComponent<Props, Stat slug }); } - case ALM_KEYS.GITHUB: { + case AlmKeys.GitHub: { const repository = almSpecificFields && almSpecificFields.repository; if (!repository) { return Promise.reject(); @@ -170,7 +170,7 @@ export default class PRDecorationBinding extends React.PureComponent<Props, Stat }); } - case ALM_KEYS.GITLAB: + case AlmKeys.GitLab: return setProjectGitlabBinding({ almSetting, project diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx index 5fbb23c1d3c..4a8b1222469 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx @@ -27,7 +27,7 @@ import AlertSuccessIcon from 'sonar-ui-common/components/icons/AlertSuccessIcon' import { Alert } from 'sonar-ui-common/components/ui/Alert'; import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner'; import { translate } from 'sonar-ui-common/helpers/l10n'; -import { AlmSettingsInstance, ALM_KEYS, ProjectAlmBinding } from '../../../../types/alm-settings'; +import { AlmKeys, AlmSettingsInstance, ProjectAlmBinding } from '../../../../types/alm-settings'; export interface PRDecorationBindingRendererProps { formData: ProjectAlmBinding; @@ -179,7 +179,7 @@ export default function PRDecorationBindingRenderer(props: PRDecorationBindingRe /> </div> - {alm === ALM_KEYS.BITBUCKET && ( + {alm === AlmKeys.Bitbucket && ( <> {renderField({ help: true, @@ -216,7 +216,7 @@ export default function PRDecorationBindingRenderer(props: PRDecorationBindingRe </> )} - {alm === ALM_KEYS.GITHUB && + {alm === AlmKeys.GitHub && renderField({ help: true, helpParams: { example: 'SonarSource/sonarqube' }, diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBinding-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBinding-test.tsx index b6447586e5a..4e2e06bdc13 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBinding-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBinding-test.tsx @@ -27,12 +27,12 @@ import { setProjectAzureBinding, setProjectBitbucketBinding, setProjectGithubBinding -} from '../../../../../api/almSettings'; +} from '../../../../../api/alm-settings'; import { mockComponent } from '../../../../../helpers/testMocks'; -import { ALM_KEYS } from '../../../../../types/alm-settings'; +import { AlmKeys } from '../../../../../types/alm-settings'; import PRDecorationBinding from '../PRDecorationBinding'; -jest.mock('../../../../../api/almSettings', () => ({ +jest.mock('../../../../../api/alm-settings', () => ({ getAlmSettings: jest.fn().mockResolvedValue([]), getProjectAlmBinding: jest.fn().mockResolvedValue(undefined), setProjectAzureBinding: jest.fn().mockResolvedValue(undefined), @@ -53,7 +53,7 @@ it('should render correctly', () => { it('should fill selects and fill formdata', async () => { const url = 'github.com'; - const instances = [{ key: 'instance1', url, alm: ALM_KEYS.GITHUB }]; + const instances = [{ key: 'instance1', url, alm: AlmKeys.GitHub }]; const formdata = { key: 'instance1', repository: 'account/repo' @@ -89,9 +89,9 @@ it('should handle reset', async () => { describe('handleSubmit', () => { const instances = [ - { key: 'github', alm: ALM_KEYS.GITHUB }, - { key: 'azure', alm: ALM_KEYS.AZURE }, - { key: 'bitbucket', alm: ALM_KEYS.BITBUCKET } + { key: 'github', alm: AlmKeys.GitHub }, + { key: 'azure', alm: AlmKeys.Azure }, + { key: 'bitbucket', alm: AlmKeys.Bitbucket } ]; it('should work for github', async () => { @@ -195,9 +195,9 @@ it('should validate form', async () => { wrapper.setState({ instances: [ - { key: 'azure', alm: ALM_KEYS.AZURE }, - { key: 'bitbucket', alm: ALM_KEYS.BITBUCKET }, - { key: 'github', alm: ALM_KEYS.GITHUB } + { key: 'azure', alm: AlmKeys.Azure }, + { key: 'bitbucket', alm: AlmKeys.Bitbucket }, + { key: 'github', alm: AlmKeys.GitHub } ] }); expect(wrapper.instance().validateForm({ key: 'azure' })).toBe(true); diff --git a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBindingRenderer-test.tsx b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBindingRenderer-test.tsx index 19c5dc38a21..dbdbca4378d 100644 --- a/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBindingRenderer-test.tsx +++ b/server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/__tests__/PRDecorationBindingRenderer-test.tsx @@ -21,7 +21,7 @@ import { shallow } from 'enzyme'; import * as React from 'react'; import Select from 'sonar-ui-common/components/controls/Select'; import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils'; -import { ALM_KEYS } from '../../../../../types/alm-settings'; +import { AlmKeys } from '../../../../../types/alm-settings'; import PRDecorationBindingRenderer, { PRDecorationBindingRendererProps } from '../PRDecorationBindingRenderer'; @@ -35,7 +35,7 @@ it('should render single instance correctly', () => { const singleInstance = { key: 'single', url: 'http://single.url', - alm: ALM_KEYS.GITHUB + alm: AlmKeys.GitHub }; expect( shallowRender({ @@ -49,22 +49,22 @@ it('should render multiple instances correctly', () => { const urls = ['http://github.enterprise.com', 'http://bbs.enterprise.com']; const instances = [ { - alm: ALM_KEYS.GITHUB, + alm: AlmKeys.GitHub, key: 'i1', url: urls[0] }, { - alm: ALM_KEYS.GITHUB, + alm: AlmKeys.GitHub, key: 'i2', url: urls[0] }, { - alm: ALM_KEYS.BITBUCKET, + alm: AlmKeys.Bitbucket, key: 'i3', url: urls[1] }, { - alm: ALM_KEYS.AZURE, + alm: AlmKeys.Azure, key: 'i4' } ]; @@ -96,7 +96,7 @@ it('should render multiple instances correctly', () => { it('should display action state correctly', () => { const urls = ['http://url.com']; - const instances = [{ key: 'key', url: urls[0], alm: ALM_KEYS.GITHUB }]; + const instances = [{ key: 'key', url: urls[0], alm: AlmKeys.GitHub }]; expect(shallowRender({ instances, loading: false, saving: true })).toMatchSnapshot(); expect(shallowRender({ instances, loading: false, success: true })).toMatchSnapshot(); @@ -112,11 +112,11 @@ it('should display action state correctly', () => { it('should render select options correctly', async () => { const instances = [ { - alm: ALM_KEYS.AZURE, + alm: AlmKeys.Azure, key: 'azure' }, { - alm: ALM_KEYS.GITHUB, + alm: AlmKeys.GitHub, key: 'github', url: 'gh.url.com' } diff --git a/server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts b/server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts index 6f3320db435..3940ca053da 100644 --- a/server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts +++ b/server/sonar-web/src/main/js/helpers/mocks/alm-settings.ts @@ -18,8 +18,8 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import { + AlmKeys, AlmSettingsInstance, - ALM_KEYS, AzureBindingDefinition, BitbucketBindingDefinition, GithubBindingDefinition, @@ -30,7 +30,7 @@ export function mockAlmSettingsInstance( overrides: Partial<AlmSettingsInstance> = {} ): AlmSettingsInstance { return { - alm: ALM_KEYS.GITHUB, + alm: AlmKeys.GitHub, key: 'key', ...overrides }; diff --git a/server/sonar-web/src/main/js/types/alm-settings.ts b/server/sonar-web/src/main/js/types/alm-settings.ts index 427e13293e8..1ff3849237e 100644 --- a/server/sonar-web/src/main/js/types/alm-settings.ts +++ b/server/sonar-web/src/main/js/types/alm-settings.ts @@ -17,46 +17,33 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -export const enum ALM_KEYS { - AZURE = 'azure', - BITBUCKET = 'bitbucket', - GITHUB = 'github', - GITLAB = 'gitlab' +export const enum AlmKeys { + Azure = 'azure', + Bitbucket = 'bitbucket', + GitHub = 'github', + GitLab = 'gitlab' } -export interface AlmSettingsBinding { +export interface AlmBindingDefinition { key: string; } -export interface AlmSettingsInstance { - alm: ALM_KEYS; - key: string; - url?: string; -} - -export interface AlmSettingsBindingDefinitions { - azure: AzureBindingDefinition[]; - bitbucket: BitbucketBindingDefinition[]; - github: GithubBindingDefinition[]; - gitlab: GitlabBindingDefinition[]; -} - -export interface AzureBindingDefinition extends AlmSettingsBinding { +export interface AzureBindingDefinition extends AlmBindingDefinition { personalAccessToken: string; } -export interface BitbucketBindingDefinition extends AlmSettingsBinding { +export interface BitbucketBindingDefinition extends AlmBindingDefinition { personalAccessToken: string; url: string; } -export interface GithubBindingDefinition extends AlmSettingsBinding { +export interface GithubBindingDefinition extends AlmBindingDefinition { appId: string; privateKey: string; url: string; } -export interface GitlabBindingDefinition extends AlmSettingsBinding { +export interface GitlabBindingDefinition extends AlmBindingDefinition { personalAccessToken: string; } @@ -88,3 +75,16 @@ export interface GitlabProjectAlmBinding { almSetting: string; project: string; } + +export interface AlmSettingsInstance { + alm: AlmKeys; + key: string; + url?: string; +} + +export interface AlmSettingsBindingDefinitions { + azure: AzureBindingDefinition[]; + bitbucket: BitbucketBindingDefinition[]; + github: GithubBindingDefinition[]; + gitlab: GitlabBindingDefinition[]; +} diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 12ff218a47f..171b69d4e0a 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -1016,55 +1016,64 @@ settings.new_code_period.description2=This setting is the default for all projec settings.languages.select_a_language_placeholder=Select a language -settings.pr_decoration.category=Pull Request Decoration -settings.pr_decoration.title=Pull Request Decoration -settings.pr_decoration.description=Pull Request Decoration adds SonarQube analysis and a Quality Gate to your Pull Requests directly in your ALM provider's interface. -settings.pr_decoration.manage_instances=Manage instances -settings.pr_decoration.azure.info=Accounts that will be used to decorate Pull Requests need Code: Read & Write permission. {link} -settings.pr_decoration.bitbucket.info=Accounts that will be used to decorate Pull Requests need write permission. {link} -settings.pr_decoration.github.info=You need to install a GitHub App with specific settings and permissions to enable Pull Request Decoration on your Organization or Repository. {link} -settings.pr_decoration.gitlab.info=Accounts that will be used to decorate Merge Requests need comment permissions on projects. The personal key needs the API scope permission. {link} -settings.pr_decoration.table.title=Pull Request Decoration configurations -settings.mr_decoration.table.title=Merge Request Decoration configurations -settings.pr_decoration.table.empty.azure=Create your first Azure DevOps configuration to enable Pull Request Decoration on your projects. -settings.pr_decoration.table.empty.bitbucket=Create your first Bitbucket configuration to enable Pull Request Decoration on your projects. -settings.pr_decoration.table.empty.github=Create your first GitHub configuration to enable Pull Request Decoration on your organization or repository. -settings.pr_decoration.table.empty.gitlab=Create your first GitLab configuration to enable Merge Request Decoration on your repository. -settings.pr_decoration.table.create=Create configuration -settings.pr_decoration.table.column.name=Name -settings.pr_decoration.table.column.bitbucket.url=Bitbucket Server URL -settings.pr_decoration.table.column.github.url=GitHub Enterprise or GitHub.com URL -settings.pr_decoration.table.column.app_id=App ID -settings.pr_decoration.table.column.edit=Edit -settings.pr_decoration.table.column.delete=Delete -settings.pr_decoration.delete.header=Delete configuration -settings.pr_decoration.delete.message=Are you sure you want to delete the {id} configuration? -settings.pr_decoration.delete.info={0} projects will no longer get Pull Request Decorations. -settings.pr_decoration.delete.no_info=An unknown number of projects will no longer get Pull Request Decorations. -settings.pr_decoration.form.header.create=Create a Pull Request Decoration configuration -settings.mr_decoration.form.header.create=Create a Merge Request Decoration configuration -settings.pr_decoration.form.header.edit=Edit the Pull Request Decoration configuration -settings.mr_decoration.form.header.edit=Edit the Merge Request Decoration configuration -settings.pr_decoration.form.name.azure=Configuration name -settings.pr_decoration.form.name.azure.help=Give your configuration a clear and succinct name. This name will be used at project level to identify the correct configured Azure instance for a project. -settings.pr_decoration.form.name.bitbucket=Configuration name -settings.pr_decoration.form.name.bitbucket.help=Give your configuration a clear and succinct name. This name will be used at project level to identify the correct configured Bitbucket instance for a project. -settings.pr_decoration.form.name.github=Configuration name -settings.pr_decoration.form.name.github.help=Give your configuration a clear and succinct name. This name will be used at project level to identify the correct configured GitHub App for a project. -settings.pr_decoration.form.name.gitlab=Configuration name -settings.pr_decoration.form.name.gitlab.help=Give your configuration a clear and succinct name. This name will be used at project level to identify the correct configured GitLab instance for a project. -settings.pr_decoration.form.url.bitbucket=Bitbucket Server URL -settings.pr_decoration.form.url.bitbucket.help=Example: {example} -settings.pr_decoration.form.url.github=GitHub URL -settings.pr_decoration.form.url.github.help1=Example for Github Enterprise: -settings.pr_decoration.form.url.github.help2=If using GitHub.com: -settings.pr_decoration.form.app_id=GitHub App ID -settings.pr_decoration.form.private_key=Private Key -settings.pr_decoration.form.personal_access_token=Personal Access token -settings.pr_decoration.form.personal_access_token.azure.help=Token of the user that will be used to decorate the Pull Requests. Needs authorized scope: "Code (read and write)". -settings.pr_decoration.form.personal_access_token.gitlab.help=Token of the user that will be used to decorate the Merge Requests. Needs API scope authorization. -settings.pr_decoration.form.save=Save configuration -settings.pr_decoration.form.cancel=Cancel +settings.almintegration.title=Integration configurations +settings.almintegration.description=ALM integrations allow SonarQube to interact with your ALM. This enables things like authentication, or providing analysis details and a Quality Gate to your Pull Requests directly in your ALM provider's interface. +settings.almintegration.manage_instances=Manage instances +settings.almintegration.azure.info=Accounts that will be used to decorate Pull Requests need Code: Read & Write permission. {link} +settings.almintegration.github.info=You need to install a GitHub App with specific settings and permissions to enable Pull Request Decoration on your Organization or Repository. {link} +settings.almintegration.gitlab.info=Accounts that will be used to decorate Merge Requests need comment permissions on projects. The personal key needs the API scope permission. {link} +settings.almintegration.bitbucket.help_1=SonarQube needs a Personal Access Token to communicate with Bitbucket Server. This token will be used to decorate Pull Requests. +settings.almintegration.bitbucket.help_2=The account used for integration needs write permission. +settings.almintegration.bitbucket.help_3=We recommend to integrate with SonarQube using a Bitbucket Server Service Account. +settings.almintegration.table.title=ALM integration configurations +settings.almintegration.table.empty.azure=Create your first Azure DevOps configuration to enable Pull Request Decoration on your projects. +settings.almintegration.table.empty.bitbucket=Create your first Bitbucket configuration to enable Pull Request Decoration on your projects. +settings.almintegration.table.empty.github=Create your first GitHub configuration to enable Pull Request Decoration on your organization or repository. +settings.almintegration.table.empty.gitlab=Create your first GitLab configuration to enable Merge Request Decoration on your repository. +settings.almintegration.table.create=Create configuration +settings.almintegration.table.column.name=Name +settings.almintegration.table.column.bitbucket.url=Bitbucket Server URL +settings.almintegration.table.column.github.url=GitHub Enterprise or GitHub.com URL +settings.almintegration.table.column.app_id=App ID +settings.almintegration.table.column.edit=Edit +settings.almintegration.table.column.delete=Delete +settings.almintegration.delete.header=Delete configuration +settings.almintegration.delete.message=Are you sure you want to delete the {id} configuration? +settings.almintegration.delete.info={0} projects will no longer get Pull Request Decorations. +settings.almintegration.delete.no_info=An unknown number of projects will no longer get Pull Request Decorations. +settings.almintegration.form.header.create=Create a configuration +settings.almintegration.form.header.edit=Edit the configuration +settings.almintegration.form.name.azure=Configuration name +settings.almintegration.form.name.azure.help=Give your configuration a clear and succinct name. This name will be used at project level to identify the correct configured Azure instance for a project. +settings.almintegration.form.name.bitbucket=Configuration name +settings.almintegration.form.name.bitbucket.help=Give your configuration a clear and succinct name. This name will be used at project level to identify the correct configured Bitbucket instance for a project. +settings.almintegration.form.name.github=Configuration name +settings.almintegration.form.name.github.help=Give your configuration a clear and succinct name. This name will be used at project level to identify the correct configured GitHub App for a project. +settings.almintegration.form.name.gitlab=Configuration name +settings.almintegration.form.name.gitlab.help=Give your configuration a clear and succinct name. This name will be used at project level to identify the correct configured GitLab instance for a project. +settings.almintegration.form.url.bitbucket=Bitbucket Server URL +settings.almintegration.form.url.bitbucket.help=Example: {example} +settings.almintegration.form.url.github=GitHub URL +settings.almintegration.form.url.github.help1=Example for Github Enterprise: +settings.almintegration.form.url.github.help2=If using GitHub.com: +settings.almintegration.form.app_id=GitHub App ID +settings.almintegration.form.private_key=Private Key +settings.almintegration.form.personal_access_token=Personal Access token +settings.almintegration.form.personal_access_token.azure.help=Token of the user that will be used to decorate the Pull Requests. Needs authorized scope: "Code (read and write)". +settings.almintegration.form.personal_access_token.gitlab.help=Token of the user that will be used to decorate the Merge Requests. Needs API scope authorization. +settings.almintegration.form.save=Save configuration +settings.almintegration.form.cancel=Cancel +settings.almintegration.features=ALM integration features +settings.almintegration.feature.enabled=This feature is enabled +settings.almintegration.feature.disabled=This feature is currently disabled +settings.almintegration.feature.need_at_least_1_binding=You need to have at least 1 binding configured to use this feature +settings.almintegration.feature.pr_decoration.title=Pull Request Decoration +settings.almintegration.feature.pr_decoration.description=Add analysis and a Quality Gate to your Pull Requests directly in your ALM provider's interface. +settings.almintegration.feature.mr_decoration.title=Merge Request Decoration +settings.almintegration.feature.mr_decoration.description=Add analysis and a Quality Gate to your Merge Requests directly in your ALM provider's interface. +settings.almintegration.feature.alm_repo_import.title=Import repositories from your ALM +settings.almintegration.feature.alm_repo_import.description=Select repositories from your ALM, and import them into SonarQube. +settings.almintegration.feature.alm_repo_import.disabled_if_multiple_bbs_instances=Connecting to multiple Bitbucket Server instances will deactivate the {feature} feature. Projects will have to be set up manually. settings.pr_decoration.binding.category=Pull Request Decoration settings.pr_decoration.binding.no_bindings=This feature must first be enabled in the global settings. {link} @@ -1088,13 +1097,14 @@ property.category.general.databaseCleaner=Database Cleaner property.category.general.looknfeel=Look & Feel property.category.general.issues=Issues property.category.general.subProjects=Sub-projects +property.category.almintegration=ALM Integrations +property.category.almintegration.github=GitHub Authentication +property.category.almintegration.github.description=In order to enable authentication on GitHub.com or GitHub Enterprise:<ul><li>SonarQube must be publicly accessible through HTTPS only</li><li>The property 'sonar.core.serverBaseURL' must be set to this public HTTPS URL</li><li>In your GitHub profile, you need to create a Developer Application for which the 'Authorization callback URL' must be set to <code>'<value_of_sonar.core.serverBaseURL_property>/oauth2/callback'</code>.</li></ul> +property.category.almintegration.gitlab=GitLab Authentication +property.category.almintegration.gitlab.description=In order to enable GitLab authentication, the property 'sonar.core.serverBaseURL' must be set to the public URL property.category.organizations=Organizations property.category.security=Security property.category.security.encryption=Encryption -property.category.security.github=GitHub -property.category.security.github.description=In order to enable authentication on GitHub.com or GitHub Enterprise:<ul><li>SonarQube must be publicly accessible through HTTPS only</li><li>The property 'sonar.core.serverBaseURL' must be set to this public HTTPS URL</li><li>In your GitHub profile, you need to create a Developer Application for which the 'Authorization callback URL' must be set to <code>'<value_of_sonar.core.serverBaseURL_property>/oauth2/callback'</code>.</li></ul> -property.category.security.gitlab=Gitlab -property.category.security.gitlab.description=In order to enable Gitlab authentication, the property 'sonar.core.serverBaseURL' must be set to the public URL property.category.security.saml=SAML property.category.security.saml.description=In order to enable SAML authentication, the property 'sonar.core.serverBaseURL' must be set to the public URL property.category.java=Java diff --git a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java index e409ccad25e..e670edbce3f 100644 --- a/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java +++ b/sonar-plugin-api/src/main/java/org/sonar/api/CoreProperties.java @@ -57,6 +57,11 @@ public interface CoreProperties { String SUBCATEGORY_DUPLICATIONS = "duplications"; /** + * @since 8.2 + */ + String CATEGORY_ALM_INTEGRATION = "almintegration"; + + /** * @since 8.1 */ String CATEGORY_HOUSEKEEPING = "housekeeping"; |