Browse Source

SONAR-12515 UI for azure PR decoration configuration

tags/8.1.0.31237
Jeremy Davis 4 years ago
parent
commit
7c707a301f
53 changed files with 1975 additions and 740 deletions
  1. 8
    0
      server/sonar-web/src/main/js/api/almSettings.ts
  2. 72
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmDefinitionFormField.tsx
  3. 23
    15
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmPRDecorationFormModal.tsx
  4. 3
    91
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmPRDecorationFormModalRenderer.tsx
  5. 54
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AzureFormModal.tsx
  6. 89
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AzureTab.tsx
  7. 61
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AzureTabRenderer.tsx
  8. 72
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AzureTable.tsx
  9. 1
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/DeleteModal.tsx
  10. 72
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GithubFormModal.tsx
  11. 21
    49
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GithubTab.tsx
  12. 61
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GithubTabRenderer.tsx
  13. 70
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GithubTable.tsx
  14. 0
    71
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/PRDecorationTable.tsx
  15. 37
    10
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/PRDecorationTabs.tsx
  16. 37
    1
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/PullRequestDecoration.tsx
  17. 3
    39
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/TabHeader.tsx
  18. 57
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmDefinitionFormField-test.tsx
  19. 7
    6
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmPRDecorationFormModal-test.tsx
  20. 3
    7
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmPRDecorationFormModalRenderer-test.tsx
  21. 38
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AzureFormModal-test.tsx
  22. 96
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AzureTab-test.tsx
  23. 44
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AzureTabRenderer-test.tsx
  24. 40
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AzureTable-test.tsx
  25. 38
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GithubFormModal-test.tsx
  26. 27
    55
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GithubTab-test.tsx
  27. 6
    8
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GithubTabRenderer-test.tsx
  28. 4
    5
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GithubTable-test.tsx
  29. 9
    4
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/PRDecorationTabs-test.tsx
  30. 30
    2
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/PullRequestDecoration-test.tsx
  31. 32
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/TabHeader-test.tsx
  32. 87
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmDefinitionFormField-test.tsx.snap
  33. 0
    10
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmPRDecorationFormModal-test.tsx.snap
  34. 1
    103
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmPRDecorationFormModalRenderer-test.tsx.snap
  35. 69
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AzureFormModal-test.tsx.snap
  36. 13
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AzureTab-test.tsx.snap
  37. 58
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AzureTabRenderer-test.tsx.snap
  38. 79
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AzureTable-test.tsx.snap
  39. 2
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/DeleteModal-test.tsx.snap
  40. 141
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GithubFormModal-test.tsx.snap
  41. 3
    4
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GithubTab-test.tsx.snap
  42. 60
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GithubTabRenderer-test.tsx.snap
  43. 89
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/GithubTable-test.tsx.snap
  44. 0
    95
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/PRDecorationTable-test.tsx.snap
  45. 143
    3
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/PRDecorationTabs-test.tsx.snap
  46. 4
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/PullRequestDecoration-test.tsx.snap
  47. 79
    0
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/TabHeader-test.tsx.snap
  48. 0
    154
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/TabRenderer-test.tsx.snap
  49. 5
    1
      server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx
  50. 5
    5
      server/sonar-web/src/main/js/apps/settings/utils.ts
  51. 10
    0
      server/sonar-web/src/main/js/helpers/testMocks.ts
  52. 9
    2
      server/sonar-web/src/main/js/types/alm-settings.d.ts
  53. 3
    0
      sonar-core/src/main/resources/org/sonar/l10n/core.properties

+ 8
- 0
server/sonar-web/src/main/js/api/almSettings.ts View File

@@ -38,6 +38,14 @@ export function updateGithubConfiguration(data: T.GithubBindingDefinition & { ne
return post('/api/alm_settings/update_github', data).catch(throwGlobalError);
}

export function createAzureConfiguration(data: T.AzureBindingDefinition) {
return post('/api/alm_settings/create_azure', data).catch(throwGlobalError);
}

export function updateAzureConfiguration(data: T.AzureBindingDefinition & { newKey: string }) {
return post('/api/alm_settings/update_azure', data).catch(throwGlobalError);
}

export function deleteConfiguration(key: string) {
return post('/api/alm_settings/delete', { key }).catch(throwGlobalError);
}

+ 72
- 0
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmDefinitionFormField.tsx View File

@@ -0,0 +1,72 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip';
import { translate } from 'sonar-ui-common/helpers/l10n';

export interface AlmDefinitionFormFieldProps<B extends T.AlmSettingsBinding> {
autoFocus?: boolean;
formData: B;
help: boolean;
id: string;
isTextArea: boolean;
maxLength: number;
onFieldChange: (id: keyof B, value: string) => void;
propKey: keyof B;
}

export function AlmDefinitionFormField<B extends T.AlmSettingsBinding>(
props: AlmDefinitionFormFieldProps<B>
) {
const { autoFocus, formData, help, id, isTextArea, maxLength, onFieldChange, propKey } = props;

return (
<div className="modal-field">
<label className="display-flex-center" htmlFor={id}>
{translate('settings.pr_decoration.form', id)}
<em className="mandatory spacer-right">*</em>
{help && <HelpTooltip overlay={translate('settings.pr_decoration.form', id, 'help')} />}
</label>
{isTextArea ? (
<textarea
className="settings-large-input"
id="privateKey"
maxLength={maxLength}
onChange={e => onFieldChange(propKey, e.currentTarget.value)}
required={true}
rows={5}
value={String(formData[propKey])}
/>
) : (
<input
autoFocus={autoFocus}
className="input-super-large"
id={id}
maxLength={maxLength}
name={id}
onChange={e => onFieldChange(propKey, e.currentTarget.value)}
size={50}
type="text"
value={String(formData[propKey])}
/>
)}
</div>
);
}

+ 23
- 15
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmPRDecorationFormModal.tsx View File

@@ -20,25 +20,32 @@
import * as React from 'react';
import AlmPRDecorationFormModalRenderer from './AlmPRDecorationFormModalRenderer';

interface Props {
alm: string;
bindingDefinition: T.GithubBindingDefinition;
interface ChildrenProps<AlmBindingDefinitionType> {
formData: AlmBindingDefinitionType;
onFieldChange: (fieldId: keyof AlmBindingDefinitionType, value: string) => void;
}

interface Props<B> {
children: (props: ChildrenProps<B>) => React.ReactNode;
bindingDefinition: B;
onCancel: () => void;
onSubmit: (bindingDefinition: T.GithubBindingDefinition, originalKey: string) => void;
onSubmit: (data: B, originalKey: string) => void;
}

interface State {
formData: T.GithubBindingDefinition;
interface State<AlmBindingDefinitionType> {
formData: AlmBindingDefinitionType;
}

export default class AlmPRDecorationFormModal extends React.PureComponent<Props, State> {
constructor(props: Props) {
export default class AlmPRDecorationFormModal<
B extends T.AlmSettingsBinding
> extends React.PureComponent<Props<B>, State<B>> {
constructor(props: Props<B>) {
super(props);

this.state = { formData: props.bindingDefinition };
}

handleFieldChange = (fieldId: keyof T.GithubBindingDefinition, value: string) => {
handleFieldChange = (fieldId: keyof B, value: string) => {
this.setState(({ formData }) => ({
formData: {
...formData,
@@ -59,19 +66,20 @@ export default class AlmPRDecorationFormModal extends React.PureComponent<Props,
};

render() {
const { alm, bindingDefinition } = this.props;
const { children, bindingDefinition } = this.props;
const { formData } = this.state;

return (
<AlmPRDecorationFormModalRenderer
alm={alm}
canSubmit={this.canSubmit}
formData={formData}
onCancel={this.props.onCancel}
onFieldChange={this.handleFieldChange}
onSubmit={this.handleFormSubmit}
originalKey={bindingDefinition.key}
/>
originalKey={bindingDefinition.key}>
{children({
formData,
onFieldChange: this.handleFieldChange
})}
</AlmPRDecorationFormModalRenderer>
);
}
}

+ 3
- 91
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AlmPRDecorationFormModalRenderer.tsx View File

@@ -19,69 +19,20 @@
*/
import * as React from 'react';
import { ResetButtonLink, SubmitButton } from 'sonar-ui-common/components/controls/buttons';
import HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip';
import SimpleModal from 'sonar-ui-common/components/controls/SimpleModal';
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
import { translate } from 'sonar-ui-common/helpers/l10n';
import { ALM_KEYS } from '../../utils';

export interface AlmPRDecorationFormModalProps {
alm: string;
canSubmit: () => boolean;
formData: T.GithubBindingDefinition;
children: React.ReactNode;
onCancel: () => void;
onSubmit: () => void;
onFieldChange: (id: string, value: string) => void;
originalKey: string;
}

function renderField(params: {
autoFocus?: boolean;
formData: T.GithubBindingDefinition;
help: boolean;
id: string;
isTextArea: boolean;
maxLength: number;
onFieldChange: (id: string, value: string) => void;
propKey: keyof T.GithubBindingDefinition;
}) {
const { autoFocus, formData, help, id, isTextArea, maxLength, onFieldChange, propKey } = params;
return (
<div className="modal-field">
<label className="display-flex-center" htmlFor={id}>
{translate('settings.pr_decoration.form', id)}
<em className="mandatory spacer-right">*</em>
{help && <HelpTooltip overlay={translate('settings.pr_decoration.form', id, 'help')} />}
</label>
{isTextArea ? (
<textarea
className="settings-large-input"
id="privateKey"
maxLength={maxLength}
onChange={e => onFieldChange(propKey, e.currentTarget.value)}
required={true}
rows={5}
value={formData[propKey]}
/>
) : (
<input
autoFocus={autoFocus}
className="input-super-large"
id={id}
maxLength={maxLength}
name={id}
onChange={e => onFieldChange(propKey, e.currentTarget.value)}
size={50}
type="text"
value={formData[propKey]}
/>
)}
</div>
);
}

export default function AlmPRDecorationFormModalRenderer(props: AlmPRDecorationFormModalProps) {
const { alm, formData, onFieldChange, originalKey } = props;
const { children, originalKey } = props;
const header = translate('settings.pr_decoration.form.header', originalKey ? 'edit' : 'create');

return (
@@ -92,46 +43,7 @@ export default function AlmPRDecorationFormModalRenderer(props: AlmPRDecorationF
<h2>{header}</h2>
</div>

<div className="modal-body modal-container">
{renderField({
autoFocus: true,
id: 'name',
formData,
propKey: 'key',
maxLength: 40,
onFieldChange,
help: true,
isTextArea: false
})}
{renderField({
id: `url.${alm}`,
formData,
propKey: 'url',
maxLength: 2000,
onFieldChange,
help: false,
isTextArea: false
})}
{alm === ALM_KEYS.GITHUB &&
renderField({
id: 'app_id',
formData,
propKey: 'appId',
maxLength: 80,
onFieldChange,
help: false,
isTextArea: false
})}
{renderField({
id: 'private_key',
formData,
propKey: 'privateKey',
maxLength: 2000,
onFieldChange,
help: false,
isTextArea: true
})}
</div>
<div className="modal-body modal-container">{children}</div>

<div className="modal-foot">
<DeferredSpinner className="spacer-right" loading={submitting} />

+ 54
- 0
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AzureFormModal.tsx View File

@@ -0,0 +1,54 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { AlmDefinitionFormField } from './AlmDefinitionFormField';

export interface AzureFormModalProps {
formData: T.AzureBindingDefinition;
onFieldChange: (fieldId: keyof T.AzureBindingDefinition, value: string) => void;
}

export default function AzureFormModal(props: AzureFormModalProps) {
const { formData, onFieldChange } = props;

return (
<>
<AlmDefinitionFormField
autoFocus={true}
formData={formData}
help={true}
id="name"
isTextArea={false}
maxLength={40}
onFieldChange={onFieldChange}
propKey="key"
/>
<AlmDefinitionFormField
formData={formData}
help={true}
id="personal_access_token"
isTextArea={true}
maxLength={2000}
onFieldChange={onFieldChange}
propKey="personalAccessToken"
/>
</>
);
}

+ 89
- 0
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AzureTab.tsx View File

@@ -0,0 +1,89 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { createAzureConfiguration, updateAzureConfiguration } from '../../../../api/almSettings';
import AzureTabRenderer from './AzureTabRenderer';

interface Props {
definitions: T.AzureBindingDefinition[];
loading: boolean;
onDelete: (definitionKey: string) => void;
onUpdateDefinitions: () => void;
}

interface State {
editedDefinition?: T.AzureBindingDefinition;
projectCount?: number;
}

export default class AzureTab extends React.PureComponent<Props, State> {
mounted = false;
state: State = {};

componentDidMount() {
this.mounted = true;
}

componentWillUnmount() {
this.mounted = false;
}

handleCancel = () => {
this.setState({
editedDefinition: undefined
});
};

handleCreate = () => {
this.setState({ editedDefinition: { key: '', personalAccessToken: '' } });
};

handleEdit = (config: T.AzureBindingDefinition) => {
this.setState({ editedDefinition: config });
};

handleSubmit = (config: T.AzureBindingDefinition, originalKey: string) => {
const call = originalKey
? updateAzureConfiguration({ newKey: config.key, ...config, key: originalKey })
: createAzureConfiguration(config);
return call.then(this.props.onUpdateDefinitions).then(() => {
if (this.mounted) {
this.setState({ editedDefinition: undefined });
}
});
};

render() {
const { definitions, loading } = this.props;
const { editedDefinition } = this.state;
return (
<AzureTabRenderer
definitions={definitions}
editedDefinition={editedDefinition}
loading={loading}
onCancel={this.handleCancel}
onCreate={this.handleCreate}
onDelete={this.props.onDelete}
onEdit={this.handleEdit}
onSubmit={this.handleSubmit}
/>
);
}
}

+ 61
- 0
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AzureTabRenderer.tsx View File

@@ -0,0 +1,61 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { ALM_KEYS } from '../../utils';
import AlmPRDecorationFormModal from './AlmPRDecorationFormModal';
import AzureFormModal from './AzureFormModal';
import AzureTable from './AzureTable';
import TabHeader from './TabHeader';

export interface AzureTabRendererProps {
editedDefinition?: T.AzureBindingDefinition;
definitions: T.AzureBindingDefinition[];
loading: boolean;
onCancel: () => void;
onCreate: () => void;
onDelete: (definitionKey: string) => void;
onEdit: (config: T.AzureBindingDefinition) => void;
onSubmit: (config: T.AzureBindingDefinition, originalKey: string) => void;
}

export default function AzureTabRenderer(props: AzureTabRendererProps) {
const { definitions, editedDefinition, loading } = props;
return (
<>
<TabHeader alm={ALM_KEYS.AZURE} onCreate={props.onCreate} />

<AzureTable
definitions={definitions}
loading={loading}
onDelete={props.onDelete}
onEdit={props.onEdit}
/>

{editedDefinition && (
<AlmPRDecorationFormModal
bindingDefinition={editedDefinition}
onCancel={props.onCancel}
onSubmit={props.onSubmit}>
{childProps => <AzureFormModal {...childProps} />}
</AlmPRDecorationFormModal>
)}
</>
);
}

+ 72
- 0
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/AzureTable.tsx View File

@@ -0,0 +1,72 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { ButtonIcon, DeleteButton } from 'sonar-ui-common/components/controls/buttons';
import EditIcon from 'sonar-ui-common/components/icons/EditIcon';
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
import { translate } from 'sonar-ui-common/helpers/l10n';

export interface AzureTableProps {
definitions: T.AzureBindingDefinition[];
loading: boolean;
onDelete: (definitionKey: string) => void;
onEdit: (config: T.AzureBindingDefinition) => void;
}

export default function AzureTable(props: AzureTableProps) {
const { definitions, loading } = props;

if (loading) {
return <DeferredSpinner />;
}

return (
<table className="data zebra spacer-bottom">
<thead>
<tr>
<th>{translate('settings.pr_decoration.table.column.name')}</th>
<th className="thin">{translate('settings.pr_decoration.table.column.edit')}</th>
<th className="thin">{translate('settings.pr_decoration.table.column.delete')}</th>
</tr>
</thead>
<tbody>
{definitions.length < 1 ? (
<tr>
<td colSpan={3}>{translate('settings.pr_decoration.table.empty.azure')}</td>
</tr>
) : (
definitions.map(definition => (
<tr key={definition.key}>
<td>{definition.key}</td>
<td>
<ButtonIcon onClick={() => props.onEdit(definition)}>
<EditIcon />
</ButtonIcon>
</td>
<td>
<DeleteButton onClick={() => props.onDelete(definition.key)} />
</td>
</tr>
))
)}
</tbody>
</table>
);
}

+ 1
- 0
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/DeleteModal.tsx View File

@@ -45,6 +45,7 @@ export default function DeleteModal({ id, onDelete, onCancel, projectCount }: De
confirmButtonText={translate('delete')}
confirmData={id}
header={translate('settings.pr_decoration.delete.header')}
isDestructive={true}
onClose={onCancel}
onConfirm={onDelete}>
<>

+ 72
- 0
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GithubFormModal.tsx View File

@@ -0,0 +1,72 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { AlmDefinitionFormField } from './AlmDefinitionFormField';

export interface GithubFormModalProps {
formData: T.GithubBindingDefinition;
onFieldChange: (fieldId: keyof T.GithubBindingDefinition, value: string) => void;
}

export default function GithubFormModal(props: GithubFormModalProps) {
const { formData, onFieldChange } = props;

return (
<>
<AlmDefinitionFormField
autoFocus={true}
formData={formData}
help={true}
id="name"
isTextArea={false}
maxLength={40}
onFieldChange={onFieldChange}
propKey="key"
/>
<AlmDefinitionFormField
formData={formData}
help={false}
id="url.github"
isTextArea={false}
maxLength={2000}
onFieldChange={onFieldChange}
propKey="url"
/>
<AlmDefinitionFormField
formData={formData}
help={false}
id="app_id"
isTextArea={false}
maxLength={80}
onFieldChange={onFieldChange}
propKey="appId"
/>
<AlmDefinitionFormField
formData={formData}
help={false}
id="private_key"
isTextArea={true}
maxLength={2000}
onFieldChange={onFieldChange}
propKey="privateKey"
/>
</>
);
}

+ 21
- 49
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GithubTab.tsx View File

@@ -18,23 +18,18 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import {
countBindedProjects,
createGithubConfiguration,
deleteConfiguration,
updateGithubConfiguration
} from '../../../../api/almSettings';
import { ALM_KEYS } from '../../utils';
import TabRenderer from './TabRenderer';
import { createGithubConfiguration, updateGithubConfiguration } from '../../../../api/almSettings';
import GithubTabRenderer from './GithubTabRenderer';

interface Props {
definitions: T.GithubBindingDefinition[];
loading: boolean;
onDelete: (definitionKey: string) => void;
onUpdateDefinitions: () => void;
}

interface State {
definitionInEdition?: T.GithubBindingDefinition;
definitionKeyForDeletion?: string;
editedDefinition?: T.GithubBindingDefinition;
projectCount?: number;
}

@@ -52,67 +47,44 @@ export default class GithubTab extends React.PureComponent<Props, State> {

handleCancel = () => {
this.setState({
definitionKeyForDeletion: undefined,
definitionInEdition: undefined,
projectCount: undefined
editedDefinition: undefined
});
};

deleteConfiguration = (id: string) => {
return deleteConfiguration(id)
.then(this.props.onUpdateDefinitions)
.then(() => {
if (this.mounted) {
this.setState({ definitionKeyForDeletion: undefined });
}
});
};

handleCreate = () => {
this.setState({ definitionInEdition: { key: '', appId: '', url: '', privateKey: '' } });
};

handleDelete = (config: T.GithubBindingDefinition) => {
this.setState({ definitionKeyForDeletion: config.key });

return countBindedProjects(config.key).then(projectCount => {
if (this.mounted) {
this.setState({ projectCount });
}
});
this.setState({ editedDefinition: { key: '', appId: '', url: '', privateKey: '' } });
};

handleEdit = (config: T.GithubBindingDefinition) => {
this.setState({ definitionInEdition: config });
this.setState({ editedDefinition: config });
};

handleSubmit = (config: T.GithubBindingDefinition, originalKey: string) => {
const call = originalKey
? updateGithubConfiguration({ newKey: config.key, ...config, key: originalKey })
: createGithubConfiguration(config);
return call.then(this.props.onUpdateDefinitions).then(() => {
if (this.mounted) {
this.setState({ definitionInEdition: undefined });
}
});
return call
.then(() => {
if (this.mounted) {
this.setState({ editedDefinition: undefined });
}
})
.then(this.props.onUpdateDefinitions);
};

render() {
const { definitions } = this.props;
const { definitionKeyForDeletion, definitionInEdition, projectCount } = this.state;
const { definitions, loading } = this.props;
const { editedDefinition } = this.state;
return (
<TabRenderer
alm={ALM_KEYS.GITHUB}
definitionInEdition={definitionInEdition}
definitionKeyForDeletion={definitionKeyForDeletion}
<GithubTabRenderer
definitions={definitions}
editedDefinition={editedDefinition}
loading={loading}
onCancel={this.handleCancel}
onConfirmDelete={this.deleteConfiguration}
onCreate={this.handleCreate}
onDelete={this.handleDelete}
onDelete={this.props.onDelete}
onEdit={this.handleEdit}
onSubmit={this.handleSubmit}
projectCount={projectCount}
/>
);
}

+ 61
- 0
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GithubTabRenderer.tsx View File

@@ -0,0 +1,61 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { ALM_KEYS } from '../../utils';
import AlmPRDecorationFormModal from './AlmPRDecorationFormModal';
import GithubFormModal from './GithubFormModal';
import GithubTable from './GithubTable';
import TabHeader from './TabHeader';

export interface GithubTabRendererProps {
editedDefinition?: T.GithubBindingDefinition;
definitions: T.GithubBindingDefinition[];
loading: boolean;
onCancel: () => void;
onCreate: () => void;
onDelete: (definitionKey: string) => void;
onEdit: (config: T.GithubBindingDefinition) => void;
onSubmit: (config: T.GithubBindingDefinition, originalKey: string) => void;
}

export default function GithubTabRenderer(props: GithubTabRendererProps) {
const { definitions, editedDefinition, loading } = props;
return (
<>
<TabHeader alm={ALM_KEYS.GITHUB} onCreate={props.onCreate} />

<GithubTable
definitions={definitions}
loading={loading}
onDelete={props.onDelete}
onEdit={props.onEdit}
/>

{editedDefinition && (
<AlmPRDecorationFormModal
bindingDefinition={editedDefinition}
onCancel={props.onCancel}
onSubmit={props.onSubmit}>
{childProps => <GithubFormModal {...childProps} />}
</AlmPRDecorationFormModal>
)}
</>
);
}

+ 70
- 0
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/GithubTable.tsx View File

@@ -0,0 +1,70 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { ButtonIcon, DeleteButton } from 'sonar-ui-common/components/controls/buttons';
import EditIcon from 'sonar-ui-common/components/icons/EditIcon';
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
import { translate } from 'sonar-ui-common/helpers/l10n';

export interface GithubTableProps {
definitions: T.GithubBindingDefinition[];
loading: boolean;
onDelete: (definitionKey: string) => void;
onEdit: (config: T.GithubBindingDefinition) => void;
}

export default function GithubTable(props: GithubTableProps) {
const { definitions, loading } = props;

if (loading) {
return <DeferredSpinner />;
}

return (
<table className="data zebra spacer-bottom">
<thead>
<tr>
<th>{translate('settings.pr_decoration.table.column.name')}</th>
<th>{translate(`settings.pr_decoration.table.column.github.url`)}</th>
<th>{translate('settings.pr_decoration.table.column.app_id')}</th>
<th className="thin">{translate('settings.pr_decoration.table.column.edit')}</th>
<th className="thin">{translate('settings.pr_decoration.table.column.delete')}</th>
</tr>
</thead>
<tbody>
{definitions.map(definition => (
<tr key={definition.key}>
<td>{definition.key}</td>
<td>{definition.url}</td>
<td>{definition.appId}</td>
<td>
<ButtonIcon onClick={() => props.onEdit(definition)}>
<EditIcon />
</ButtonIcon>
</td>
<td>
<DeleteButton onClick={() => props.onDelete(definition.key)} />
</td>
</tr>
))}
</tbody>
</table>
);
}

+ 0
- 71
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/PRDecorationTable.tsx View File

@@ -1,71 +0,0 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { ButtonIcon } from 'sonar-ui-common/components/controls/buttons';
import DeleteIcon from 'sonar-ui-common/components/icons/DeleteIcon';
import EditIcon from 'sonar-ui-common/components/icons/EditIcon';
import { translate } from 'sonar-ui-common/helpers/l10n';
import { ALM_KEYS } from '../../utils';

export interface PRDecorationTableProps {
alm: ALM_KEYS;
definitions: T.GithubBindingDefinition[];
onDelete: (config: T.GithubBindingDefinition) => void;
onEdit: (config: T.GithubBindingDefinition) => void;
}

export default function PRDecorationTable(props: PRDecorationTableProps) {
const { alm, definitions } = props;

return (
<>
<table className="data zebra spacer-bottom">
<thead>
<tr>
<th>{translate('settings.pr_decoration.table.column.name')}</th>
<th>{translate(`settings.pr_decoration.table.column.${alm}.url`)}</th>
<th>{translate('settings.pr_decoration.table.column.app_id')}</th>
<th className="thin">{translate('settings.pr_decoration.table.column.edit')}</th>
<th className="thin">{translate('settings.pr_decoration.table.column.delete')}</th>
</tr>
</thead>
<tbody>
{definitions.map(definition => (
<tr key={definition.key}>
<td>{definition.key}</td>
<td>{definition.url}</td>
<td>{definition.appId}</td>
<td>
<ButtonIcon onClick={() => props.onEdit(definition)}>
<EditIcon />
</ButtonIcon>
</td>
<td>
<ButtonIcon onClick={() => props.onDelete(definition)}>
<DeleteIcon />
</ButtonIcon>
</td>
</tr>
))}
</tbody>
</table>
</>
);
}

+ 37
- 10
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/PRDecorationTabs.tsx View File

@@ -19,25 +19,27 @@
*/
import * as React from 'react';
import BoxedTabs from 'sonar-ui-common/components/controls/BoxedTabs';
import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
import { translate } from 'sonar-ui-common/helpers/l10n';
import { almName, ALM_KEYS } from '../../utils';
import AzureTab from './AzureTab';
import DeleteModal from './DeleteModal';
import GithubTab from './GithubTab';

export interface PRDecorationTabsProps {
currentAlm: ALM_KEYS;
definitionKeyForDeletion?: string;
definitions: T.AlmSettingsBindingDefinitions;
loading: boolean;
onCancel: () => void;
onConfirmDelete: (definitionKey: string) => void;
onDelete: (definitionKey: string) => void;
onSelectAlm: (alm: ALM_KEYS) => void;
onUpdateDefinitions: () => void;
projectCount?: number;
}

export default function PRDecorationTabs(props: PRDecorationTabsProps) {
const { definitions, currentAlm, loading } = props;

if (loading) {
return <DeferredSpinner />;
}
const { definitionKeyForDeletion, definitions, currentAlm, loading, projectCount } = props;

return (
<>
@@ -55,16 +57,41 @@ export default function PRDecorationTabs(props: PRDecorationTabsProps) {
{
key: ALM_KEYS.GITHUB,
label: almName[ALM_KEYS.GITHUB]
},
{
key: ALM_KEYS.AZURE,
label: almName[ALM_KEYS.AZURE]
}
]}
/>

<div className="boxed-group boxed-group-inner">
<GithubTab
definitions={definitions.github}
onUpdateDefinitions={props.onUpdateDefinitions}
/>
{currentAlm === ALM_KEYS.AZURE && (
<AzureTab
definitions={definitions.azure}
loading={loading}
onDelete={props.onDelete}
onUpdateDefinitions={props.onUpdateDefinitions}
/>
)}
{currentAlm === ALM_KEYS.GITHUB && (
<GithubTab
definitions={definitions.github}
loading={loading}
onDelete={props.onDelete}
onUpdateDefinitions={props.onUpdateDefinitions}
/>
)}
</div>

{definitionKeyForDeletion && (
<DeleteModal
id={definitionKeyForDeletion}
onCancel={props.onCancel}
onDelete={props.onConfirmDelete}
projectCount={projectCount}
/>
)}
</>
);
}

+ 37
- 1
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/PullRequestDecoration.tsx View File

@@ -18,14 +18,20 @@
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import * as React from 'react';
import { getAlmDefinitions } from '../../../../api/almSettings';
import {
countBindedProjects,
deleteConfiguration,
getAlmDefinitions
} from '../../../../api/almSettings';
import { ALM_KEYS } from '../../utils';
import PRDecorationTabs from './PRDecorationTabs';

interface State {
currentAlm: ALM_KEYS;
definitionKeyForDeletion?: string;
definitions: T.AlmSettingsBindingDefinitions;
loading: boolean;
projectCount?: number;
}

export default class PullRequestDecoration extends React.PureComponent<{}, State> {
@@ -33,6 +39,7 @@ export default class PullRequestDecoration extends React.PureComponent<{}, State
state: State = {
currentAlm: ALM_KEYS.GITHUB,
definitions: {
[ALM_KEYS.AZURE]: [],
[ALM_KEYS.GITHUB]: []
},
loading: true
@@ -47,7 +54,18 @@ export default class PullRequestDecoration extends React.PureComponent<{}, State
this.mounted = false;
}

deleteConfiguration = (definitionKey: string) => {
return deleteConfiguration(definitionKey)
.then(() => {
if (this.mounted) {
this.setState({ definitionKeyForDeletion: undefined, projectCount: undefined });
}
})
.then(this.fetchPullRequestDecorationSetting);
};

fetchPullRequestDecorationSetting = () => {
this.setState({ loading: true });
return getAlmDefinitions()
.then(definitions => {
if (this.mounted) {
@@ -68,9 +86,27 @@ export default class PullRequestDecoration extends React.PureComponent<{}, State
this.setState({ currentAlm });
};

handleCancel = () => {
this.setState({ definitionKeyForDeletion: undefined, projectCount: undefined });
};

handleDelete = (definitionKey: string) => {
return countBindedProjects(definitionKey).then(projectCount => {
if (this.mounted) {
this.setState({
definitionKeyForDeletion: definitionKey,
projectCount
});
}
});
};

render() {
return (
<PRDecorationTabs
onCancel={this.handleCancel}
onConfirmDelete={this.deleteConfiguration}
onDelete={this.handleDelete}
onSelectAlm={this.handleSelectAlm}
onUpdateDefinitions={this.fetchPullRequestDecorationSetting}
{...this.state}

server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/TabRenderer.tsx → server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/TabHeader.tsx View File

@@ -24,26 +24,14 @@ import { Button } from 'sonar-ui-common/components/controls/buttons';
import { Alert } from 'sonar-ui-common/components/ui/Alert';
import { translate } from 'sonar-ui-common/helpers/l10n';
import { ALM_KEYS } from '../../utils';
import AlmPRDecorationFormModal from './AlmPRDecorationFormModal';
import DeleteModal from './DeleteModal';
import PRDecorationTable from './PRDecorationTable';

export interface TabRendererProps {
export interface TabHeaderProps {
alm: ALM_KEYS;
definitionInEdition?: T.GithubBindingDefinition;
definitionKeyForDeletion?: string;
definitions: T.GithubBindingDefinition[];
onCancel: () => void;
onConfirmDelete: (id: string) => void;
onCreate: () => void;
onDelete: (config: T.GithubBindingDefinition) => void;
onEdit: (config: T.GithubBindingDefinition) => void;
onSubmit: (config: T.GithubBindingDefinition, originalKey: string) => void;
projectCount?: number;
}

export default function TabRenderer(props: TabRendererProps) {
const { alm, definitions, definitionKeyForDeletion, definitionInEdition, projectCount } = props;
export default function TabHeader(props: TabHeaderProps) {
const { alm } = props;
return (
<>
<Alert className="spacer-top huge-spacer-bottom" variant="info">
@@ -64,30 +52,6 @@ export default function TabRenderer(props: TabRendererProps) {
<h4 className="display-inline">{translate('settings.pr_decoration.table.title')}</h4>
<Button onClick={props.onCreate}>{translate('settings.pr_decoration.table.create')}</Button>
</div>

<PRDecorationTable
alm={alm}
definitions={definitions}
onDelete={props.onDelete}
onEdit={props.onEdit}
/>
{definitionKeyForDeletion && (
<DeleteModal
id={definitionKeyForDeletion}
onCancel={props.onCancel}
onDelete={props.onConfirmDelete}
projectCount={projectCount}
/>
)}

{definitionInEdition && (
<AlmPRDecorationFormModal
alm={ALM_KEYS.GITHUB}
bindingDefinition={definitionInEdition}
onCancel={props.onCancel}
onSubmit={props.onSubmit}
/>
)}
</>
);
}

+ 57
- 0
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmDefinitionFormField-test.tsx View File

@@ -0,0 +1,57 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { shallow } from 'enzyme';
import * as React from 'react';
import { AlmDefinitionFormField, AlmDefinitionFormFieldProps } from '../AlmDefinitionFormField';

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

it('should call onFieldChange', () => {
const onInputChange = jest.fn();
shallowRender({ onFieldChange: onInputChange })
.find('input')
.simulate('change', { currentTarget: { value: '' } });
expect(onInputChange).toBeCalled();

const onTextAreaChange = jest.fn();
shallowRender({ isTextArea: true, onFieldChange: onTextAreaChange })
.find('textarea')
.simulate('change', { currentTarget: { value: '' } });
expect(onTextAreaChange).toBeCalled();
});

function shallowRender(props: Partial<AlmDefinitionFormFieldProps<T.AlmSettingsBinding>> = {}) {
return shallow(
<AlmDefinitionFormField
formData={{ key: 'key' }}
help={false}
id="key"
isTextArea={false}
maxLength={40}
onFieldChange={jest.fn()}
propKey="key"
{...props}
/>
);
}

+ 7
- 6
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmPRDecorationFormModal-test.tsx View File

@@ -21,7 +21,6 @@ import { shallow } from 'enzyme';
import * as React from 'react';
import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
import { mockGithubDefinition } from '../../../../../helpers/testMocks';
import { ALM_KEYS } from '../../../utils';
import AlmPRDecorationFormModal from '../AlmPRDecorationFormModal';

it('should render correctly', () => {
@@ -75,14 +74,16 @@ it('should (dis)allow submit by validating its state', async () => {
expect(wrapper.instance().canSubmit()).toBe(true);
});

function shallowRender(props: Partial<AlmPRDecorationFormModal['props']> = {}) {
return shallow<AlmPRDecorationFormModal>(
function shallowRender(
props: Partial<AlmPRDecorationFormModal<T.GithubBindingDefinition>['props']> = {}
) {
return shallow<AlmPRDecorationFormModal<T.GithubBindingDefinition>>(
<AlmPRDecorationFormModal
alm={ALM_KEYS.GITHUB}
bindingDefinition={{ appId: '', key: '', privateKey: '', url: '' }}
onCancel={jest.fn()}
onSubmit={jest.fn()}
{...props}
/>
{...props}>
{() => null}
</AlmPRDecorationFormModal>
);
}

+ 3
- 7
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AlmPRDecorationFormModalRenderer-test.tsx View File

@@ -19,8 +19,6 @@
*/
import { shallow } from 'enzyme';
import * as React from 'react';
import { mockGithubDefinition } from '../../../../../helpers/testMocks';
import { ALM_KEYS } from '../../../utils';
import AlmPRDecorationFormModalRenderer, {
AlmPRDecorationFormModalProps
} from '../AlmPRDecorationFormModalRenderer';
@@ -32,14 +30,12 @@ it('should render correctly', () => {
function shallowRender(props: Partial<AlmPRDecorationFormModalProps> = {}) {
return shallow(
<AlmPRDecorationFormModalRenderer
alm={ALM_KEYS.GITHUB}
canSubmit={jest.fn()}
formData={mockGithubDefinition()}
onCancel={jest.fn()}
onFieldChange={jest.fn()}
onSubmit={jest.fn()}
originalKey=""
{...props}
/>
{...props}>
{() => null}
</AlmPRDecorationFormModalRenderer>
);
}

+ 38
- 0
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AzureFormModal-test.tsx View File

@@ -0,0 +1,38 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { shallow } from 'enzyme';
import * as React from 'react';
import { mockAzureDefinition } from '../../../../../helpers/testMocks';
import AzureFormModal, { AzureFormModalProps } from '../AzureFormModal';

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

function shallowRender(props: Partial<AzureFormModalProps> = {}) {
return shallow(
<AzureFormModal
formData={{ key: '', personalAccessToken: '' }}
onFieldChange={jest.fn()}
{...props}
/>
);
}

+ 96
- 0
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AzureTab-test.tsx View File

@@ -0,0 +1,96 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { shallow } from 'enzyme';
import * as React from 'react';
import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
import { createAzureConfiguration, updateAzureConfiguration } from '../../../../../api/almSettings';
import { mockAzureDefinition } from '../../../../../helpers/testMocks';
import AzureTab from '../AzureTab';

jest.mock('../../../../../api/almSettings', () => ({
countBindedProjects: jest.fn().mockResolvedValue(2),
createAzureConfiguration: jest.fn().mockResolvedValue({}),
deleteConfiguration: jest.fn().mockResolvedValue({}),
updateAzureConfiguration: jest.fn().mockResolvedValue({})
}));

beforeEach(() => {
jest.clearAllMocks();
});

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

it('should handle cancel', async () => {
const wrapper = shallowRender();

wrapper.setState({
editedDefinition: mockAzureDefinition()
});

wrapper.instance().handleCancel();

await waitAndUpdate(wrapper);

expect(wrapper.state().editedDefinition).toBeUndefined();
});

it('should create config', async () => {
const onUpdateDefinitions = jest.fn();
const config = mockAzureDefinition();
const wrapper = shallowRender({ onUpdateDefinitions });
wrapper.setState({ editedDefinition: config });

await wrapper.instance().handleSubmit(config, '');

expect(createAzureConfiguration).toBeCalledWith(config);
expect(onUpdateDefinitions).toBeCalled();
expect(wrapper.state().editedDefinition).toBeUndefined();
});

it('should update config', async () => {
const onUpdateDefinitions = jest.fn();
const config = mockAzureDefinition();
const wrapper = shallowRender({ onUpdateDefinitions });
wrapper.setState({ editedDefinition: config });

await wrapper.instance().handleSubmit(config, 'originalKey');

expect(updateAzureConfiguration).toBeCalledWith({
newKey: 'key',
...config,
key: 'originalKey'
});
expect(onUpdateDefinitions).toBeCalled();
expect(wrapper.state().editedDefinition).toBeUndefined();
});

function shallowRender(props: Partial<AzureTab['props']> = {}) {
return shallow<AzureTab>(
<AzureTab
definitions={[]}
loading={false}
onDelete={jest.fn()}
onUpdateDefinitions={jest.fn()}
{...props}
/>
);
}

+ 44
- 0
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AzureTabRenderer-test.tsx View File

@@ -0,0 +1,44 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { shallow } from 'enzyme';
import * as React from 'react';
import { mockAzureDefinition } from '../../../../../helpers/testMocks';
import AzureTabRenderer, { AzureTabRendererProps } from '../AzureTabRenderer';

it('should render correctly', () => {
expect(shallowRender({ loading: true })).toMatchSnapshot();
expect(shallowRender()).toMatchSnapshot();
expect(shallowRender({ editedDefinition: mockAzureDefinition() })).toMatchSnapshot();
});

function shallowRender(props: Partial<AzureTabRendererProps> = {}) {
return shallow(
<AzureTabRenderer
definitions={[]}
loading={false}
onCancel={jest.fn()}
onCreate={jest.fn()}
onDelete={jest.fn()}
onEdit={jest.fn()}
onSubmit={jest.fn()}
{...props}
/>
);
}

+ 40
- 0
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/AzureTable-test.tsx View File

@@ -0,0 +1,40 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { shallow } from 'enzyme';
import * as React from 'react';
import { mockAzureDefinition } from '../../../../../helpers/testMocks';
import AzureTable, { AzureTableProps } from '../AzureTable';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
expect(shallowRender({ definitions: [mockAzureDefinition()] })).toMatchSnapshot();
});

function shallowRender(props: Partial<AzureTableProps> = {}) {
return shallow(
<AzureTable
definitions={[]}
loading={false}
onDelete={jest.fn()}
onEdit={jest.fn()}
{...props}
/>
);
}

+ 38
- 0
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GithubFormModal-test.tsx View File

@@ -0,0 +1,38 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { shallow } from 'enzyme';
import * as React from 'react';
import { mockGithubDefinition } from '../../../../../helpers/testMocks';
import GithubFormModal, { GithubFormModalProps } from '../GithubFormModal';

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

function shallowRender(props: Partial<GithubFormModalProps> = {}) {
return shallow(
<GithubFormModal
formData={{ key: '', appId: '', privateKey: '', url: '' }}
onFieldChange={jest.fn()}
{...props}
/>
);
}

+ 27
- 55
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GithubTab-test.tsx View File

@@ -22,7 +22,6 @@ import * as React from 'react';
import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
import {
createGithubConfiguration,
deleteConfiguration,
updateGithubConfiguration
} from '../../../../../api/almSettings';
import { mockGithubDefinition } from '../../../../../helpers/testMocks';
@@ -47,81 +46,54 @@ it('should handle cancel', async () => {
const wrapper = shallowRender();

wrapper.setState({
definitionKeyForDeletion: '12321',
definitionInEdition: mockGithubDefinition()
editedDefinition: mockGithubDefinition()
});

wrapper.instance().handleCancel();

await waitAndUpdate(wrapper);

expect(wrapper.state().definitionKeyForDeletion).toBeUndefined();
expect(wrapper.state().definitionInEdition).toBeUndefined();
});

it('should delete config', async () => {
const onUpdateDefinitions = jest.fn();
const wrapper = shallowRender({ onUpdateDefinitions });
wrapper.setState({ definitionKeyForDeletion: '123' });

await wrapper
.instance()
.deleteConfiguration('123')
.then(() => {
expect(deleteConfiguration).toBeCalledWith('123');
expect(onUpdateDefinitions).toBeCalled();
expect(wrapper.state().definitionKeyForDeletion).toBeUndefined();
});
expect(wrapper.state().editedDefinition).toBeUndefined();
});

it('should create config', async () => {
const onUpdateDefinitions = jest.fn();
const config = {
key: 'new conf',
url: 'ewrqewr',
appId: '3742985',
privateKey: 'rt7r78ew6t87ret'
};
const config = mockGithubDefinition();
const wrapper = shallowRender({ onUpdateDefinitions });
wrapper.setState({ definitionInEdition: config });
wrapper.setState({ editedDefinition: config });

await wrapper.instance().handleSubmit(config, '');

await wrapper
.instance()
.handleSubmit(config, '')
.then(() => {
expect(createGithubConfiguration).toBeCalledWith(config);
expect(onUpdateDefinitions).toBeCalled();
expect(wrapper.state().definitionInEdition).toBeUndefined();
});
expect(createGithubConfiguration).toBeCalledWith(config);
expect(onUpdateDefinitions).toBeCalled();
expect(wrapper.state().editedDefinition).toBeUndefined();
});

it('should update config', async () => {
const onUpdateDefinitions = jest.fn();
const config = {
key: 'new conf',
url: 'ewrqewr',
appId: '3742985',
privateKey: 'rt7r78ew6t87ret'
};
const config = mockGithubDefinition();
const wrapper = shallowRender({ onUpdateDefinitions });
wrapper.setState({ definitionInEdition: config });
wrapper.setState({ editedDefinition: config });

await wrapper
.instance()
.handleSubmit(config, 'originalKey')
.then(() => {
expect(updateGithubConfiguration).toBeCalledWith({
newKey: 'new conf',
...config,
key: 'originalKey'
});
expect(onUpdateDefinitions).toBeCalled();
expect(wrapper.state().definitionInEdition).toBeUndefined();
});
await wrapper.instance().handleSubmit(config, 'originalKey');

expect(updateGithubConfiguration).toBeCalledWith({
newKey: 'key',
...config,
key: 'originalKey'
});
expect(onUpdateDefinitions).toBeCalled();
expect(wrapper.state().editedDefinition).toBeUndefined();
});

function shallowRender(props: Partial<GithubTab['props']> = {}) {
return shallow<GithubTab>(
<GithubTab definitions={[]} onUpdateDefinitions={jest.fn()} {...props} />
<GithubTab
definitions={[]}
loading={false}
onDelete={jest.fn()}
onUpdateDefinitions={jest.fn()}
{...props}
/>
);
}

server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/TabRenderer-test.tsx → server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GithubTabRenderer-test.tsx View File

@@ -20,22 +20,20 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { mockGithubDefinition } from '../../../../../helpers/testMocks';
import { ALM_KEYS } from '../../../utils';
import TabRenderer, { TabRendererProps } from '../TabRenderer';
import GithubTabRenderer, { GithubTabRendererProps } from '../GithubTabRenderer';

it('should render correctly', () => {
expect(shallowRender({ loading: true })).toMatchSnapshot();
expect(shallowRender()).toMatchSnapshot();
expect(shallowRender({ definitionKeyForDeletion: '123' })).toMatchSnapshot();
expect(shallowRender({ definitionInEdition: mockGithubDefinition() })).toMatchSnapshot();
expect(shallowRender({ editedDefinition: mockGithubDefinition() })).toMatchSnapshot();
});

function shallowRender(props: Partial<TabRendererProps> = {}) {
function shallowRender(props: Partial<GithubTabRendererProps> = {}) {
return shallow(
<TabRenderer
alm={ALM_KEYS.GITHUB}
<GithubTabRenderer
definitions={[]}
loading={false}
onCancel={jest.fn()}
onConfirmDelete={jest.fn()}
onCreate={jest.fn()}
onDelete={jest.fn()}
onEdit={jest.fn()}

server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/PRDecorationTable-test.tsx → server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/GithubTable-test.tsx View File

@@ -20,19 +20,18 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { mockGithubDefinition } from '../../../../../helpers/testMocks';
import { ALM_KEYS } from '../../../utils';
import PRDecorationTable, { PRDecorationTableProps } from '../PRDecorationTable';
import GithubTable, { GithubTableProps } from '../GithubTable';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
expect(shallowRender({ definitions: [mockGithubDefinition()] })).toMatchSnapshot();
});

function shallowRender(props: Partial<PRDecorationTableProps> = {}) {
function shallowRender(props: Partial<GithubTableProps> = {}) {
return shallow(
<PRDecorationTable
alm={ALM_KEYS.GITHUB}
<GithubTable
definitions={[]}
loading={false}
onDelete={jest.fn()}
onEdit={jest.fn()}
{...props}

+ 9
- 4
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/PRDecorationTabs-test.tsx View File

@@ -23,16 +23,21 @@ import { ALM_KEYS } from '../../../utils';
import PRDecorationTabs, { PRDecorationTabsProps } from '../PRDecorationTabs';

it('should render correctly', () => {
expect(shallowRender()).toMatchSnapshot();
expect(shallowRender({ loading: false })).toMatchSnapshot();
expect(shallowRender({ loading: true })).toMatchSnapshot();
expect(shallowRender({ definitionKeyForDeletion: 'keyToDelete' })).toMatchSnapshot();
expect(shallowRender({ currentAlm: ALM_KEYS.AZURE })).toMatchSnapshot();
expect(shallowRender({ currentAlm: ALM_KEYS.GITHUB })).toMatchSnapshot();
});

function shallowRender(props: Partial<PRDecorationTabsProps> = {}) {
return shallow(
<PRDecorationTabs
currentAlm={ALM_KEYS.GITHUB}
definitions={{ github: [] }}
loading={true}
definitions={{ azure: [], github: [] }}
loading={false}
onCancel={jest.fn()}
onConfirmDelete={jest.fn()}
onDelete={jest.fn()}
onSelectAlm={jest.fn()}
onUpdateDefinitions={jest.fn()}
{...props}

+ 30
- 2
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/PullRequestDecoration-test.tsx View File

@@ -20,11 +20,17 @@
import { shallow } from 'enzyme';
import * as React from 'react';
import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
import { getAlmDefinitions } from '../../../../../api/almSettings';
import {
countBindedProjects,
deleteConfiguration,
getAlmDefinitions
} from '../../../../../api/almSettings';
import { ALM_KEYS } from '../../../utils';
import PullRequestDecoration from '../PullRequestDecoration';

jest.mock('../../../../../api/almSettings', () => ({
countBindedProjects: jest.fn().mockResolvedValue(0),
deleteConfiguration: jest.fn().mockResolvedValue(undefined),
getAlmDefinitions: jest.fn().mockResolvedValue({ github: [] })
}));

@@ -39,7 +45,7 @@ it('should render correctly', () => {
it('should handle alm selection', async () => {
const wrapper = shallowRender();

wrapper.setState({ currentAlm: ALM_KEYS.BITBUCKET });
wrapper.setState({ currentAlm: ALM_KEYS.AZURE });

wrapper.instance().handleSelectAlm(ALM_KEYS.GITHUB);

@@ -48,6 +54,28 @@ it('should handle alm selection', async () => {
expect(wrapper.state().currentAlm).toBe(ALM_KEYS.GITHUB);
});

it('should handle delete', async () => {
const toBeDeleted = '45672';
(countBindedProjects as jest.Mock).mockResolvedValueOnce(7);
const wrapper = shallowRender();

wrapper.instance().handleDelete(toBeDeleted);
await waitAndUpdate(wrapper);

expect(wrapper.state().projectCount).toBe(7);
expect(wrapper.state().definitionKeyForDeletion).toBe(toBeDeleted);
});

it('should delete configuration', async () => {
(deleteConfiguration as jest.Mock).mockResolvedValueOnce(undefined);
const wrapper = shallowRender();
wrapper.instance().deleteConfiguration('8345678');

await waitAndUpdate(wrapper);
expect(wrapper.state().projectCount).toBeUndefined();
expect(wrapper.state().definitionKeyForDeletion).toBeUndefined();
});

it('should fetch settings', async () => {
const wrapper = shallowRender();


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

@@ -0,0 +1,32 @@
/*
* SonarQube
* Copyright (C) 2009-2019 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
import { shallow } from 'enzyme';
import * as React from 'react';
import { ALM_KEYS } from '../../../utils';
import TabHeader, { TabHeaderProps } from '../TabHeader';

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

function shallowRender(alm: ALM_KEYS, props: Partial<TabHeaderProps> = {}) {
return shallow(<TabHeader alm={alm} onCreate={jest.fn()} {...props} />);
}

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

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

exports[`should render correctly 1`] = `
<div
className="modal-field"
>
<label
className="display-flex-center"
htmlFor="key"
>
settings.pr_decoration.form.key
<em
className="mandatory spacer-right"
>
*
</em>
</label>
<input
className="input-super-large"
id="key"
maxLength={40}
name="key"
onChange={[Function]}
size={50}
type="text"
value="key"
/>
</div>
`;

exports[`should render correctly 2`] = `
<div
className="modal-field"
>
<label
className="display-flex-center"
htmlFor="key"
>
settings.pr_decoration.form.key
<em
className="mandatory spacer-right"
>
*
</em>
<HelpTooltip
overlay="settings.pr_decoration.form.key.help"
/>
</label>
<input
className="input-super-large"
id="key"
maxLength={40}
name="key"
onChange={[Function]}
size={50}
type="text"
value="key"
/>
</div>
`;

exports[`should render correctly 3`] = `
<div
className="modal-field"
>
<label
className="display-flex-center"
htmlFor="key"
>
settings.pr_decoration.form.key
<em
className="mandatory spacer-right"
>
*
</em>
</label>
<textarea
className="settings-large-input"
id="privateKey"
maxLength={40}
onChange={[Function]}
required={true}
rows={5}
value="key"
/>
</div>
`;

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

@@ -2,18 +2,8 @@

exports[`should render correctly 1`] = `
<AlmPRDecorationFormModalRenderer
alm="github"
canSubmit={[Function]}
formData={
Object {
"appId": "",
"key": "",
"privateKey": "",
"url": "",
}
}
onCancel={[MockFunction]}
onFieldChange={[Function]}
onSubmit={[Function]}
originalKey=""
/>

+ 1
- 103
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecoration/__tests__/__snapshots__/AlmPRDecorationFormModalRenderer-test.tsx.snap View File

@@ -20,109 +20,7 @@ exports[`should render correctly 1`] = `
<div
className="modal-body modal-container"
>
<div
className="modal-field"
>
<label
className="display-flex-center"
htmlFor="name"
>
settings.pr_decoration.form.name
<em
className="mandatory spacer-right"
>
*
</em>
<HelpTooltip
overlay="settings.pr_decoration.form.name.help"
/>
</label>
<input
autoFocus={true}
className="input-super-large"
id="name"
maxLength={40}
name="name"
onChange={[Function]}
size={50}
type="text"
value="key"
/>
</div>
<div
className="modal-field"
>
<label
className="display-flex-center"
htmlFor="url.github"
>
settings.pr_decoration.form.url.github
<em
className="mandatory spacer-right"
>
*
</em>
</label>
<input
className="input-super-large"
id="url.github"
maxLength={2000}
name="url.github"
onChange={[Function]}
size={50}
type="text"
value="http:alm.enterprise.com"
/>
</div>
<div
className="modal-field"
>
<label
className="display-flex-center"
htmlFor="app_id"
>
settings.pr_decoration.form.app_id
<em
className="mandatory spacer-right"
>
*
</em>
</label>
<input
className="input-super-large"
id="app_id"
maxLength={80}
name="app_id"
onChange={[Function]}
size={50}
type="text"
value="123456"
/>
</div>
<div
className="modal-field"
>
<label
className="display-flex-center"
htmlFor="private_key"
>
settings.pr_decoration.form.private_key
<em
className="mandatory spacer-right"
>
*
</em>
</label>
<textarea
className="settings-large-input"
id="privateKey"
maxLength={2000}
onChange={[Function]}
required={true}
rows={5}
value="asdf1234"
/>
</div>
<Component />
</div>
<div
className="modal-foot"

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

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

exports[`should render correctly 1`] = `
<Fragment>
<AlmDefinitionFormField
autoFocus={true}
formData={
Object {
"key": "",
"personalAccessToken": "",
}
}
help={true}
id="name"
isTextArea={false}
maxLength={40}
onFieldChange={[MockFunction]}
propKey="key"
/>
<AlmDefinitionFormField
formData={
Object {
"key": "",
"personalAccessToken": "",
}
}
help={true}
id="personal_access_token"
isTextArea={true}
maxLength={2000}
onFieldChange={[MockFunction]}
propKey="personalAccessToken"
/>
</Fragment>
`;

exports[`should render correctly 2`] = `
<Fragment>
<AlmDefinitionFormField
autoFocus={true}
formData={
Object {
"key": "key",
"personalAccessToken": "asdf1234",
}
}
help={true}
id="name"
isTextArea={false}
maxLength={40}
onFieldChange={[MockFunction]}
propKey="key"
/>
<AlmDefinitionFormField
formData={
Object {
"key": "key",
"personalAccessToken": "asdf1234",
}
}
help={true}
id="personal_access_token"
isTextArea={true}
maxLength={2000}
onFieldChange={[MockFunction]}
propKey="personalAccessToken"
/>
</Fragment>
`;

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

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

exports[`should render correctly 1`] = `
<AzureTabRenderer
definitions={Array []}
loading={false}
onCancel={[Function]}
onCreate={[Function]}
onDelete={[MockFunction]}
onEdit={[Function]}
onSubmit={[Function]}
/>
`;

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

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

exports[`should render correctly 1`] = `
<Fragment>
<TabHeader
alm="azure"
onCreate={[MockFunction]}
/>
<AzureTable
definitions={Array []}
loading={true}
onDelete={[MockFunction]}
onEdit={[MockFunction]}
/>
</Fragment>
`;

exports[`should render correctly 2`] = `
<Fragment>
<TabHeader
alm="azure"
onCreate={[MockFunction]}
/>
<AzureTable
definitions={Array []}
loading={false}
onDelete={[MockFunction]}
onEdit={[MockFunction]}
/>
</Fragment>
`;

exports[`should render correctly 3`] = `
<Fragment>
<TabHeader
alm="azure"
onCreate={[MockFunction]}
/>
<AzureTable
definitions={Array []}
loading={false}
onDelete={[MockFunction]}
onEdit={[MockFunction]}
/>
<AlmPRDecorationFormModal
bindingDefinition={
Object {
"key": "key",
"personalAccessToken": "asdf1234",
}
}
onCancel={[MockFunction]}
onSubmit={[MockFunction]}
>
<Component />
</AlmPRDecorationFormModal>
</Fragment>
`;

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

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

exports[`should render correctly 1`] = `
<table
className="data zebra spacer-bottom"
>
<thead>
<tr>
<th>
settings.pr_decoration.table.column.name
</th>
<th
className="thin"
>
settings.pr_decoration.table.column.edit
</th>
<th
className="thin"
>
settings.pr_decoration.table.column.delete
</th>
</tr>
</thead>
<tbody>
<tr>
<td
colSpan={3}
>
settings.pr_decoration.table.empty.azure
</td>
</tr>
</tbody>
</table>
`;

exports[`should render correctly 2`] = `
<table
className="data zebra spacer-bottom"
>
<thead>
<tr>
<th>
settings.pr_decoration.table.column.name
</th>
<th
className="thin"
>
settings.pr_decoration.table.column.edit
</th>
<th
className="thin"
>
settings.pr_decoration.table.column.delete
</th>
</tr>
</thead>
<tbody>
<tr
key="key"
>
<td>
key
</td>
<td>
<ButtonIcon
onClick={[Function]}
>
<EditIcon />
</ButtonIcon>
</td>
<td>
<DeleteButton
onClick={[Function]}
/>
</td>
</tr>
</tbody>
</table>
`;

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

@@ -5,6 +5,7 @@ exports[`should render correctly 1`] = `
confirmButtonText="delete"
confirmData="1"
header="settings.pr_decoration.delete.header"
isDestructive={true}
onClose={[MockFunction]}
onConfirm={[MockFunction]}
>
@@ -35,6 +36,7 @@ exports[`should render correctly 2`] = `
confirmButtonText="delete"
confirmData="1"
header="settings.pr_decoration.delete.header"
isDestructive={true}
onClose={[MockFunction]}
onConfirm={[MockFunction]}
>

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

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

exports[`should render correctly 1`] = `
<Fragment>
<AlmDefinitionFormField
autoFocus={true}
formData={
Object {
"appId": "",
"key": "",
"privateKey": "",
"url": "",
}
}
help={true}
id="name"
isTextArea={false}
maxLength={40}
onFieldChange={[MockFunction]}
propKey="key"
/>
<AlmDefinitionFormField
formData={
Object {
"appId": "",
"key": "",
"privateKey": "",
"url": "",
}
}
help={false}
id="url.github"
isTextArea={false}
maxLength={2000}
onFieldChange={[MockFunction]}
propKey="url"
/>
<AlmDefinitionFormField
formData={
Object {
"appId": "",
"key": "",
"privateKey": "",
"url": "",
}
}
help={false}
id="app_id"
isTextArea={false}
maxLength={80}
onFieldChange={[MockFunction]}
propKey="appId"
/>
<AlmDefinitionFormField
formData={
Object {
"appId": "",
"key": "",
"privateKey": "",
"url": "",
}
}
help={false}
id="private_key"
isTextArea={true}
maxLength={2000}
onFieldChange={[MockFunction]}
propKey="privateKey"
/>
</Fragment>
`;

exports[`should render correctly 2`] = `
<Fragment>
<AlmDefinitionFormField
autoFocus={true}
formData={
Object {
"appId": "123456",
"key": "key",
"privateKey": "asdf1234",
"url": "http:alm.enterprise.com",
}
}
help={true}
id="name"
isTextArea={false}
maxLength={40}
onFieldChange={[MockFunction]}
propKey="key"
/>
<AlmDefinitionFormField
formData={
Object {
"appId": "123456",
"key": "key",
"privateKey": "asdf1234",
"url": "http:alm.enterprise.com",
}
}
help={false}
id="url.github"
isTextArea={false}
maxLength={2000}
onFieldChange={[MockFunction]}
propKey="url"
/>
<AlmDefinitionFormField
formData={
Object {
"appId": "123456",
"key": "key",
"privateKey": "asdf1234",
"url": "http:alm.enterprise.com",
}
}
help={false}
id="app_id"
isTextArea={false}
maxLength={80}
onFieldChange={[MockFunction]}
propKey="appId"
/>
<AlmDefinitionFormField
formData={
Object {
"appId": "123456",
"key": "key",
"privateKey": "asdf1234",
"url": "http:alm.enterprise.com",
}
}
help={false}
id="private_key"
isTextArea={true}
maxLength={2000}
onFieldChange={[MockFunction]}
propKey="privateKey"
/>
</Fragment>
`;

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

@@ -1,13 +1,12 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<TabRenderer
alm="github"
<GithubTabRenderer
definitions={Array []}
loading={false}
onCancel={[Function]}
onConfirmDelete={[Function]}
onCreate={[Function]}
onDelete={[Function]}
onDelete={[MockFunction]}
onEdit={[Function]}
onSubmit={[Function]}
/>

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

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

exports[`should render correctly 1`] = `
<Fragment>
<TabHeader
alm="github"
onCreate={[MockFunction]}
/>
<GithubTable
definitions={Array []}
loading={true}
onDelete={[MockFunction]}
onEdit={[MockFunction]}
/>
</Fragment>
`;

exports[`should render correctly 2`] = `
<Fragment>
<TabHeader
alm="github"
onCreate={[MockFunction]}
/>
<GithubTable
definitions={Array []}
loading={false}
onDelete={[MockFunction]}
onEdit={[MockFunction]}
/>
</Fragment>
`;

exports[`should render correctly 3`] = `
<Fragment>
<TabHeader
alm="github"
onCreate={[MockFunction]}
/>
<GithubTable
definitions={Array []}
loading={false}
onDelete={[MockFunction]}
onEdit={[MockFunction]}
/>
<AlmPRDecorationFormModal
bindingDefinition={
Object {
"appId": "123456",
"key": "key",
"privateKey": "asdf1234",
"url": "http:alm.enterprise.com",
}
}
onCancel={[MockFunction]}
onSubmit={[MockFunction]}
>
<Component />
</AlmPRDecorationFormModal>
</Fragment>
`;

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

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

exports[`should render correctly 1`] = `
<table
className="data zebra spacer-bottom"
>
<thead>
<tr>
<th>
settings.pr_decoration.table.column.name
</th>
<th>
settings.pr_decoration.table.column.github.url
</th>
<th>
settings.pr_decoration.table.column.app_id
</th>
<th
className="thin"
>
settings.pr_decoration.table.column.edit
</th>
<th
className="thin"
>
settings.pr_decoration.table.column.delete
</th>
</tr>
</thead>
<tbody />
</table>
`;

exports[`should render correctly 2`] = `
<table
className="data zebra spacer-bottom"
>
<thead>
<tr>
<th>
settings.pr_decoration.table.column.name
</th>
<th>
settings.pr_decoration.table.column.github.url
</th>
<th>
settings.pr_decoration.table.column.app_id
</th>
<th
className="thin"
>
settings.pr_decoration.table.column.edit
</th>
<th
className="thin"
>
settings.pr_decoration.table.column.delete
</th>
</tr>
</thead>
<tbody>
<tr
key="key"
>
<td>
key
</td>
<td>
http:alm.enterprise.com
</td>
<td>
123456
</td>
<td>
<ButtonIcon
onClick={[Function]}
>
<EditIcon />
</ButtonIcon>
</td>
<td>
<DeleteButton
onClick={[Function]}
/>
</td>
</tr>
</tbody>
</table>
`;

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

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

exports[`should render correctly 1`] = `
<Fragment>
<table
className="data zebra spacer-bottom"
>
<thead>
<tr>
<th>
settings.pr_decoration.table.column.name
</th>
<th>
settings.pr_decoration.table.column.github.url
</th>
<th>
settings.pr_decoration.table.column.app_id
</th>
<th
className="thin"
>
settings.pr_decoration.table.column.edit
</th>
<th
className="thin"
>
settings.pr_decoration.table.column.delete
</th>
</tr>
</thead>
<tbody />
</table>
</Fragment>
`;

exports[`should render correctly 2`] = `
<Fragment>
<table
className="data zebra spacer-bottom"
>
<thead>
<tr>
<th>
settings.pr_decoration.table.column.name
</th>
<th>
settings.pr_decoration.table.column.github.url
</th>
<th>
settings.pr_decoration.table.column.app_id
</th>
<th
className="thin"
>
settings.pr_decoration.table.column.edit
</th>
<th
className="thin"
>
settings.pr_decoration.table.column.delete
</th>
</tr>
</thead>
<tbody>
<tr
key="key"
>
<td>
key
</td>
<td>
http:alm.enterprise.com
</td>
<td>
123456
</td>
<td>
<ButtonIcon
onClick={[Function]}
>
<EditIcon />
</ButtonIcon>
</td>
<td>
<ButtonIcon
onClick={[Function]}
>
<DeleteIcon />
</ButtonIcon>
</td>
</tr>
</tbody>
</table>
</Fragment>
`;

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

@@ -1,9 +1,48 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`should render correctly 1`] = `
<DeferredSpinner
timeout={100}
/>
<Fragment>
<header
className="page-header"
>
<h1
className="page-title"
>
settings.pr_decoration.title
</h1>
</header>
<div
className="markdown small spacer-top big-spacer-bottom"
>
settings.pr_decoration.description
</div>
<BoxedTabs
onSelect={[MockFunction]}
selected="github"
tabs={
Array [
Object {
"key": "github",
"label": "Github Enterprise",
},
Object {
"key": "azure",
"label": "Azure DevOps Server",
},
]
}
/>
<div
className="boxed-group boxed-group-inner"
>
<GithubTab
definitions={Array []}
loading={true}
onDelete={[MockFunction]}
onUpdateDefinitions={[MockFunction]}
/>
</div>
</Fragment>
`;

exports[`should render correctly 2`] = `
@@ -31,6 +70,105 @@ exports[`should render correctly 2`] = `
"key": "github",
"label": "Github Enterprise",
},
Object {
"key": "azure",
"label": "Azure DevOps Server",
},
]
}
/>
<div
className="boxed-group boxed-group-inner"
>
<GithubTab
definitions={Array []}
loading={false}
onDelete={[MockFunction]}
onUpdateDefinitions={[MockFunction]}
/>
</div>
<DeleteModal
id="keyToDelete"
onCancel={[MockFunction]}
onDelete={[MockFunction]}
/>
</Fragment>
`;

exports[`should render correctly 3`] = `
<Fragment>
<header
className="page-header"
>
<h1
className="page-title"
>
settings.pr_decoration.title
</h1>
</header>
<div
className="markdown small spacer-top big-spacer-bottom"
>
settings.pr_decoration.description
</div>
<BoxedTabs
onSelect={[MockFunction]}
selected="azure"
tabs={
Array [
Object {
"key": "github",
"label": "Github Enterprise",
},
Object {
"key": "azure",
"label": "Azure DevOps Server",
},
]
}
/>
<div
className="boxed-group boxed-group-inner"
>
<AzureTab
definitions={Array []}
loading={false}
onDelete={[MockFunction]}
onUpdateDefinitions={[MockFunction]}
/>
</div>
</Fragment>
`;

exports[`should render correctly 4`] = `
<Fragment>
<header
className="page-header"
>
<h1
className="page-title"
>
settings.pr_decoration.title
</h1>
</header>
<div
className="markdown small spacer-top big-spacer-bottom"
>
settings.pr_decoration.description
</div>
<BoxedTabs
onSelect={[MockFunction]}
selected="github"
tabs={
Array [
Object {
"key": "github",
"label": "Github Enterprise",
},
Object {
"key": "azure",
"label": "Azure DevOps Server",
},
]
}
/>
@@ -39,6 +177,8 @@ exports[`should render correctly 2`] = `
>
<GithubTab
definitions={Array []}
loading={false}
onDelete={[MockFunction]}
onUpdateDefinitions={[MockFunction]}
/>
</div>

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

@@ -5,10 +5,14 @@ exports[`should render correctly 1`] = `
currentAlm="github"
definitions={
Object {
"azure": Array [],
"github": Array [],
}
}
loading={true}
onCancel={[Function]}
onConfirmDelete={[Function]}
onDelete={[Function]}
onSelectAlm={[Function]}
onUpdateDefinitions={[Function]}
/>

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

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

exports[`should render correctly 1`] = `
<Fragment>
<Alert
className="spacer-top huge-spacer-bottom"
variant="info"
>
<FormattedMessage
defaultMessage="settings.pr_decoration.azure.info"
id="settings.pr_decoration.azure.info"
values={
Object {
"link": <Link
onlyActiveOnIndex={false}
style={Object {}}
to="/documentation/analysis/pull-request/#pr-decoration"
>
learn_more
</Link>,
}
}
/>
</Alert>
<div
className="big-spacer-bottom display-flex-space-between"
>
<h4
className="display-inline"
>
settings.pr_decoration.table.title
</h4>
<Button
onClick={[MockFunction]}
>
settings.pr_decoration.table.create
</Button>
</div>
</Fragment>
`;

exports[`should render correctly 2`] = `
<Fragment>
<Alert
className="spacer-top huge-spacer-bottom"
variant="info"
>
<FormattedMessage
defaultMessage="settings.pr_decoration.github.info"
id="settings.pr_decoration.github.info"
values={
Object {
"link": <Link
onlyActiveOnIndex={false}
style={Object {}}
to="/documentation/analysis/pull-request/#pr-decoration"
>
learn_more
</Link>,
}
}
/>
</Alert>
<div
className="big-spacer-bottom display-flex-space-between"
>
<h4
className="display-inline"
>
settings.pr_decoration.table.title
</h4>
<Button
onClick={[MockFunction]}
>
settings.pr_decoration.table.create
</Button>
</div>
</Fragment>
`;

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

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

exports[`should render correctly 1`] = `
<Fragment>
<Alert
className="spacer-top huge-spacer-bottom"
variant="info"
>
<FormattedMessage
defaultMessage="settings.pr_decoration.github.info"
id="settings.pr_decoration.github.info"
values={
Object {
"link": <Link
onlyActiveOnIndex={false}
style={Object {}}
to="/documentation/analysis/pull-request/#pr-decoration"
>
learn_more
</Link>,
}
}
/>
</Alert>
<div
className="big-spacer-bottom display-flex-space-between"
>
<h4
className="display-inline"
>
settings.pr_decoration.table.title
</h4>
<Button
onClick={[MockFunction]}
>
settings.pr_decoration.table.create
</Button>
</div>
<PRDecorationTable
alm="github"
definitions={Array []}
onDelete={[MockFunction]}
onEdit={[MockFunction]}
/>
</Fragment>
`;

exports[`should render correctly 2`] = `
<Fragment>
<Alert
className="spacer-top huge-spacer-bottom"
variant="info"
>
<FormattedMessage
defaultMessage="settings.pr_decoration.github.info"
id="settings.pr_decoration.github.info"
values={
Object {
"link": <Link
onlyActiveOnIndex={false}
style={Object {}}
to="/documentation/analysis/pull-request/#pr-decoration"
>
learn_more
</Link>,
}
}
/>
</Alert>
<div
className="big-spacer-bottom display-flex-space-between"
>
<h4
className="display-inline"
>
settings.pr_decoration.table.title
</h4>
<Button
onClick={[MockFunction]}
>
settings.pr_decoration.table.create
</Button>
</div>
<PRDecorationTable
alm="github"
definitions={Array []}
onDelete={[MockFunction]}
onEdit={[MockFunction]}
/>
<DeleteModal
id="123"
onCancel={[MockFunction]}
onDelete={[MockFunction]}
/>
</Fragment>
`;

exports[`should render correctly 3`] = `
<Fragment>
<Alert
className="spacer-top huge-spacer-bottom"
variant="info"
>
<FormattedMessage
defaultMessage="settings.pr_decoration.github.info"
id="settings.pr_decoration.github.info"
values={
Object {
"link": <Link
onlyActiveOnIndex={false}
style={Object {}}
to="/documentation/analysis/pull-request/#pr-decoration"
>
learn_more
</Link>,
}
}
/>
</Alert>
<div
className="big-spacer-bottom display-flex-space-between"
>
<h4
className="display-inline"
>
settings.pr_decoration.table.title
</h4>
<Button
onClick={[MockFunction]}
>
settings.pr_decoration.table.create
</Button>
</div>
<PRDecorationTable
alm="github"
definitions={Array []}
onDelete={[MockFunction]}
onEdit={[MockFunction]}
/>
<AlmPRDecorationFormModal
alm="github"
bindingDefinition={
Object {
"appId": "123456",
"key": "key",
"privateKey": "asdf1234",
"url": "http:alm.enterprise.com",
}
}
onCancel={[MockFunction]}
onSubmit={[MockFunction]}
/>
</Fragment>
`;

+ 5
- 1
server/sonar-web/src/main/js/apps/settings/components/pullRequestDecorationBinding/PRDecorationBindingRenderer.tsx View File

@@ -40,6 +40,10 @@ export interface PRDecorationBindingRendererProps {
success: boolean;
}

function renderLabel(v: T.AlmSettingsInstance) {
return v.url ? `${v.key} — ${v.url}` : v.key;
}

export default function PRDecorationBindingRenderer(props: PRDecorationBindingRendererProps) {
const {
formData: { repository, key },
@@ -100,7 +104,7 @@ export default function PRDecorationBindingRenderer(props: PRDecorationBindingRe
clearable={false}
id="name"
onChange={({ value }: { value: string }) => props.onFieldChange('key', value)}
options={instances.map(v => ({ value: v.key, label: `${v.key} — ${v.url}` }))}
options={instances.map(v => ({ value: v.key, label: renderLabel(v) }))}
searchable={false}
value={key}
/>

+ 5
- 5
server/sonar-web/src/main/js/apps/settings/utils.ts View File

@@ -23,14 +23,14 @@ import { hasMessage, translate } from 'sonar-ui-common/helpers/l10n';
export const DEFAULT_CATEGORY = 'general';

export enum ALM_KEYS {
BITBUCKET = 'bitbucket',
GITHUB = 'github',
AZURE_DEVOPS = 'azure_devops'
AZURE = 'azure',
// BITBUCKET = 'bitbucket',
GITHUB = 'github'
}

export const almName = {
[ALM_KEYS.AZURE_DEVOPS]: 'Azure DevOps Server',
[ALM_KEYS.BITBUCKET]: 'Bitbucket Server',
[ALM_KEYS.AZURE]: 'Azure DevOps Server',
// [ALM_KEYS.BITBUCKET]: 'Bitbucket Server',
[ALM_KEYS.GITHUB]: 'Github Enterprise'
};


+ 10
- 0
server/sonar-web/src/main/js/helpers/testMocks.ts View File

@@ -50,6 +50,16 @@ export function mockAlmOrganization(overrides: Partial<T.AlmOrganization> = {}):
};
}

export function mockAzureDefinition(
overrides: Partial<T.AzureBindingDefinition> = {}
): T.AzureBindingDefinition {
return {
key: 'key',
personalAccessToken: 'asdf1234',
...overrides
};
}

export function mockGithubDefinition(
overrides: Partial<T.GithubBindingDefinition> = {}
): T.GithubBindingDefinition {

+ 9
- 2
server/sonar-web/src/main/js/types/alm-settings.d.ts View File

@@ -20,22 +20,29 @@
declare namespace T {
export interface AlmSettingsBinding {
key: string;
url: string;
}

export interface AlmSettingsInstance extends AlmSettingsBinding {
export interface AlmSettingsInstance {
alm: string;
key: string;
url?: string;
}

export interface AlmSettingsBindingDefinitions {
azure: AzureBindingDefinition[];
github: GithubBindingDefinition[];
}

export interface GithubBindingDefinition extends AlmSettingsBinding {
url: string;
appId: string;
privateKey: string;
}

export interface AzureBindingDefinition extends AlmSettingsBinding {
personalAccessToken: string;
}

export interface ProjectAlmBinding {
key: string;
alm: string;

+ 3
- 0
sonar-core/src/main/resources/org/sonar/l10n/core.properties View File

@@ -924,6 +924,7 @@ settings.pr_decoration.category=Pull Requests
settings.pr_decoration.title=Pull Requests decoration
settings.pr_decoration.description=When Pull Request decoration is enabled, SonarQube publishes the status of the analysis directly in your ALM Pull requests.
settings.pr_decoration.manage_instances=Manage instances
settings.pr_decoration.azure.info=The account that will be used to decorate Pull Requests needs 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.table.title=Pull Request decoration configurations
settings.pr_decoration.table.create=Create configuration
@@ -943,6 +944,8 @@ settings.pr_decoration.form.name.help=Give your configuration a clear and succin
settings.pr_decoration.form.url.github=GitHub Enterprise URL
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.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.save=Save configuration
settings.pr_decoration.form.cancel=Cancel


Loading…
Cancel
Save