aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src/main/js
diff options
context:
space:
mode:
authorJulien Lancelot <julien.lancelot@sonarsource.com>2018-10-30 08:34:54 +0100
committerSonarTech <sonartech@sonarsource.com>2018-11-16 20:21:05 +0100
commit2507163633382d56601e825ba2aadafc3edbbe9f (patch)
tree02c8d3f113653a59611637f797b11151b8701638 /server/sonar-web/src/main/js
parent02f78a025d5a3569c9b1c9e6e1cc5e170b27f91c (diff)
downloadsonarqube-2507163633382d56601e825ba2aadafc3edbbe9f.tar.gz
sonarqube-2507163633382d56601e825ba2aadafc3edbbe9f.zip
SONAR-11321 Prevent binding already bound application
Diffstat (limited to 'server/sonar-web/src/main/js')
-rw-r--r--server/sonar-web/src/main/js/api/alm-integration.ts29
-rw-r--r--server/sonar-web/src/main/js/app/types.ts1
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx43
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/AutoPersonalOrganizationBind.tsx28
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/ChooseRemoteOrganizationStep.tsx70
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx51
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/ChooseRemoteOrganizationStep-test.tsx12
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx38
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoPersonalOrganizationBind-test.tsx.snap4
-rw-r--r--server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ChooseRemoteOrganizationStep-test.tsx.snap64
10 files changed, 238 insertions, 102 deletions
diff --git a/server/sonar-web/src/main/js/api/alm-integration.ts b/server/sonar-web/src/main/js/api/alm-integration.ts
index 05ba477a887..4ede88d3bff 100644
--- a/server/sonar-web/src/main/js/api/alm-integration.ts
+++ b/server/sonar-web/src/main/js/api/alm-integration.ts
@@ -22,7 +22,8 @@ import {
AlmApplication,
AlmOrganization,
AlmRepository,
- AlmUnboundApplication
+ AlmUnboundApplication,
+ OrganizationBase
} from '../app/types';
import throwGlobalError from '../app/utils/throwGlobalError';
@@ -51,10 +52,20 @@ function fetchAlmOrganization(data: { installationId: string }, remainingTries:
);
}
-export function getAlmOrganization(data: { installationId: string }): Promise<AlmOrganization> {
- return fetchAlmOrganization(data, 5).then(({ organization }) => ({
- ...organization,
- name: organization.name || organization.key
+export interface GetAlmOrganizationResponse {
+ almOrganization: AlmOrganization;
+ boundOrganization?: OrganizationBase;
+}
+
+export function getAlmOrganization(data: {
+ installationId: string;
+}): Promise<GetAlmOrganizationResponse> {
+ return fetchAlmOrganization(data, 5).then(({ almOrganization, boundOrganization }) => ({
+ almOrganization: {
+ ...almOrganization,
+ name: almOrganization.name || almOrganization.key
+ },
+ boundOrganization
}));
}
@@ -64,8 +75,12 @@ export function getRepositories(data: {
return getJSON('/api/alm_integration/list_repositories', data).catch(throwGlobalError);
}
-export function listUnboundApplications(): Promise<{ applications: AlmUnboundApplication[] }> {
- return getJSON('/api/alm_integration/list_unbound_applications').catch(throwGlobalError);
+export function listUnboundApplications(): Promise<AlmUnboundApplication[]> {
+ return getJSON('/api/alm_integration/list_unbound_applications').then(
+ ({ applications }) =>
+ applications.map((app: AlmUnboundApplication) => ({ ...app, name: app.name || app.key })),
+ throwGlobalError
+ );
}
export function provisionProject(data: {
diff --git a/server/sonar-web/src/main/js/app/types.ts b/server/sonar-web/src/main/js/app/types.ts
index ff3b88449a0..bcca9babe86 100644
--- a/server/sonar-web/src/main/js/app/types.ts
+++ b/server/sonar-web/src/main/js/app/types.ts
@@ -42,6 +42,7 @@ export interface AlmRepository {
export interface AlmUnboundApplication {
installationId: string;
+ key: string;
name: string;
}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx b/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx
index f24b6c44f92..5adbae0b71c 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/AutoOrganizationCreate.tsx
@@ -38,8 +38,7 @@ import { getBaseUrl } from '../../../helpers/urls';
export enum Filters {
Bind = 'bind',
- Create = 'create',
- None = 'none'
+ Create = 'create'
}
interface Props {
@@ -47,6 +46,7 @@ interface Props {
almInstallId?: string;
almOrganization?: AlmOrganization;
almUnboundApplications: AlmUnboundApplication[];
+ boundOrganization?: OrganizationBase;
createOrganization: (
organization: OrganizationBase & { installationId?: string }
) => Promise<Organization>;
@@ -55,14 +55,14 @@ interface Props {
}
interface State {
- filter: Filters;
+ filter?: Filters;
}
export default class AutoOrganizationCreate extends React.PureComponent<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
- filter: props.unboundOrganizations.length === 0 ? Filters.Create : Filters.None
+ filter: props.unboundOrganizations.length === 0 ? Filters.Create : undefined
};
}
@@ -71,19 +71,16 @@ export default class AutoOrganizationCreate extends React.PureComponent<Props, S
};
handleCreateOrganization = (organization: Required<OrganizationBase>) => {
- if (organization) {
- return this.props
- .createOrganization({
- avatar: organization.avatar,
- description: organization.description,
- installationId: this.props.almInstallId,
- key: organization.key,
- name: organization.name || organization.key,
- url: organization.url
- })
- .then(({ key }) => this.props.onOrgCreated(key));
- }
- return Promise.reject();
+ return this.props
+ .createOrganization({
+ avatar: organization.avatar,
+ description: organization.description,
+ installationId: this.props.almInstallId,
+ key: organization.key,
+ name: organization.name || organization.key,
+ url: organization.url
+ })
+ .then(({ key }) => this.props.onOrgCreated(key));
};
handleBindOrganization = (organization: string) => {
@@ -97,8 +94,14 @@ export default class AutoOrganizationCreate extends React.PureComponent<Props, S
};
render() {
- const { almApplication, almInstallId, almOrganization, unboundOrganizations } = this.props;
- if (almInstallId && almOrganization) {
+ const {
+ almApplication,
+ almInstallId,
+ almOrganization,
+ boundOrganization,
+ unboundOrganizations
+ } = this.props;
+ if (almInstallId && almOrganization && !boundOrganization) {
const { filter } = this.state;
const hasUnboundOrgs = unboundOrganizations.length > 0;
return (
@@ -168,7 +171,9 @@ export default class AutoOrganizationCreate extends React.PureComponent<Props, S
<ChooseRemoteOrganizationStep
almApplication={this.props.almApplication}
almInstallId={almInstallId}
+ almOrganization={almOrganization}
almUnboundApplications={this.props.almUnboundApplications}
+ boundOrganization={boundOrganization}
/>
);
}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/AutoPersonalOrganizationBind.tsx b/server/sonar-web/src/main/js/apps/create/organization/AutoPersonalOrganizationBind.tsx
index 47a65c594d6..5524c1fc19a 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/AutoPersonalOrganizationBind.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/AutoPersonalOrganizationBind.tsx
@@ -45,20 +45,16 @@ interface Props {
export default class AutoPersonalOrganizationBind extends React.PureComponent<Props> {
handleCreateOrganization = (organization: Required<OrganizationBase>) => {
- if (organization) {
- return this.props
- .updateOrganization({
- avatar: organization.avatar,
- description: organization.description,
- installationId: this.props.almInstallId,
- key: this.props.importPersonalOrg.key,
- name: organization.name || organization.key,
- url: organization.url
- })
- .then(({ key }) => this.props.onOrgCreated(key));
- } else {
- return Promise.reject();
- }
+ return this.props
+ .updateOrganization({
+ avatar: organization.avatar,
+ description: organization.description,
+ installationId: this.props.almInstallId,
+ key: this.props.importPersonalOrg.key,
+ name: organization.name || organization.key,
+ url: organization.url
+ })
+ .then(({ key }) => this.props.onOrgCreated(key));
};
render() {
@@ -69,7 +65,7 @@ export default class AutoPersonalOrganizationBind extends React.PureComponent<Pr
onOpen={() => {}}
open={true}
organization={importPersonalOrg}>
- <p className="huge-spacer-bottom">
+ <div className="huge-spacer-bottom">
<FormattedMessage
defaultMessage={translate('onboarding.import_personal_organization_x')}
id="onboarding.import_personal_organization_x"
@@ -89,7 +85,7 @@ export default class AutoPersonalOrganizationBind extends React.PureComponent<Pr
personalName: importPersonalOrg && <strong>{importPersonalOrg.name}</strong>
}}
/>
- </p>
+ </div>
<OrganizationDetailsForm
keyReadOnly={true}
onContinue={this.handleCreateOrganization}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/ChooseRemoteOrganizationStep.tsx b/server/sonar-web/src/main/js/apps/create/organization/ChooseRemoteOrganizationStep.tsx
index 9a562150fec..02447552553 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/ChooseRemoteOrganizationStep.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/ChooseRemoteOrganizationStep.tsx
@@ -19,14 +19,21 @@
*/
import * as React from 'react';
import { WithRouterProps, withRouter } from 'react-router';
+import { FormattedMessage } from 'react-intl';
import { sortBy } from 'lodash';
import { serializeQuery } from './utils';
import IdentityProviderLink from '../../../components/ui/IdentityProviderLink';
+import OrganizationAvatar from '../../../components/common/OrganizationAvatar';
import Select from '../../../components/controls/Select';
import Step from '../../tutorials/components/Step';
import { Alert } from '../../../components/ui/Alert';
import { SubmitButton } from '../../../components/ui/buttons';
-import { AlmApplication, AlmUnboundApplication } from '../../../app/types';
+import {
+ AlmApplication,
+ AlmOrganization,
+ AlmUnboundApplication,
+ OrganizationBase
+} from '../../../app/types';
import { getBaseUrl } from '../../../helpers/urls';
import { sanitizeAlmId } from '../../../helpers/almIntegrations';
import { translate } from '../../../helpers/l10n';
@@ -34,7 +41,9 @@ import { translate } from '../../../helpers/l10n';
interface Props {
almApplication: AlmApplication;
almInstallId?: string;
+ almOrganization?: AlmOrganization;
almUnboundApplications: AlmUnboundApplication[];
+ boundOrganization?: OrganizationBase;
}
interface State {
@@ -82,19 +91,58 @@ export class ChooseRemoteOrganizationStep extends React.PureComponent<
};
renderForm = () => {
- const { almApplication, almInstallId, almUnboundApplications } = this.props;
+ const {
+ almApplication,
+ almInstallId,
+ almOrganization,
+ almUnboundApplications,
+ boundOrganization
+ } = this.props;
const { unboundInstallationId } = this.state;
return (
<div className="boxed-group-inner">
- {almInstallId && (
- <Alert className="markdown big-spacer-bottom width-60" variant="error">
- {translate('onboarding.import_organization.org_not_found')}
- <ul>
- <li>{translate('onboarding.import_organization.org_not_found.tips_1')}</li>
- <li>{translate('onboarding.import_organization.org_not_found.tips_2')}</li>
- </ul>
- </Alert>
- )}
+ {almInstallId &&
+ !almOrganization && (
+ <Alert className="big-spacer-bottom width-60" variant="error">
+ <div className="markdown">
+ {translate('onboarding.import_organization.org_not_found')}
+ <ul>
+ <li>{translate('onboarding.import_organization.org_not_found.tips_1')}</li>
+ <li>{translate('onboarding.import_organization.org_not_found.tips_2')}</li>
+ </ul>
+ </div>
+ </Alert>
+ )}
+ {almOrganization &&
+ boundOrganization && (
+ <Alert className="big-spacer-bottom width-60" variant="error">
+ <FormattedMessage
+ defaultMessage={translate('onboarding.import_organization.already_bound_x')}
+ id="onboarding.import_organization.already_bound_x"
+ values={{
+ avatar: (
+ <img
+ alt={almApplication.name}
+ className="little-spacer-left"
+ src={`${getBaseUrl()}/images/sonarcloud/${sanitizeAlmId(
+ almApplication.key
+ )}.svg`}
+ width={16}
+ />
+ ),
+ name: <strong>{almOrganization.name}</strong>,
+ boundAvatar: (
+ <OrganizationAvatar
+ className="little-spacer-left"
+ organization={boundOrganization}
+ small={true}
+ />
+ ),
+ boundName: <strong>{boundOrganization.name}</strong>
+ }}
+ />
+ </Alert>
+ )}
<div className="display-flex-center">
<div className="display-inline-block abs-width-400">
<IdentityProviderLink
diff --git a/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx b/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx
index 44be6b5bcc8..c12db4930b9 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/CreateOrganization.tsx
@@ -42,6 +42,7 @@ import {
bindAlmOrganization,
getAlmAppInfo,
getAlmOrganization,
+ GetAlmOrganizationResponse,
listUnboundApplications
} from '../../../api/alm-integration';
import { getSubscriptionPlans } from '../../../api/billing';
@@ -59,8 +60,7 @@ import { translate } from '../../../helpers/l10n';
import { get, remove } from '../../../helpers/storage';
import { slugify } from '../../../helpers/strings';
import { getOrganizationUrl } from '../../../helpers/urls';
-import { skipOnboarding as skipOnboardingAction } from '../../../store/users';
-import { skipOnboarding } from '../../../api/users';
+import { skipOnboarding } from '../../../store/users';
import * as api from '../../../api/organizations';
import * as actions from '../../../store/organizations';
import '../../../app/styles/sonarcloud.css';
@@ -76,7 +76,7 @@ interface Props {
organization: OrganizationBase & { installationId?: string }
) => Promise<Organization>;
userOrganizations: Organization[];
- skipOnboardingAction: () => void;
+ skipOnboarding: () => void;
}
interface State {
@@ -84,6 +84,7 @@ interface State {
almOrganization?: AlmOrganization;
almOrgLoading: boolean;
almUnboundApplications: AlmUnboundApplication[];
+ boundOrganization?: OrganizationBase;
loading: boolean;
organization?: Organization;
subscriptionPlans?: SubscriptionPlan[];
@@ -127,7 +128,7 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr
if (query.almInstallId) {
this.fetchAlmOrganization(query.almInstallId);
} else {
- this.setState({ almOrganization: undefined, loading: true });
+ this.setState({ almOrganization: undefined, boundOrganization: undefined, loading: true });
this.fetchAlmUnboundApplications().then(this.stopLoading, this.stopLoading);
}
}
@@ -136,6 +137,9 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr
componentWillUnmount() {
this.mounted = false;
document.body.classList.remove('white-page');
+ if (document.documentElement) {
+ document.documentElement.classList.remove('white-page');
+ }
}
fetchAlmApplication = () => {
@@ -147,14 +151,14 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr
};
fetchAlmUnboundApplications = () => {
- return listUnboundApplications().then(({ applications }) => {
+ return listUnboundApplications().then(almUnboundApplications => {
if (this.mounted) {
- this.setState({ almUnboundApplications: applications });
+ this.setState({ almUnboundApplications });
}
});
};
- fetchValidOrgKey = (almOrganization: AlmOrganization) => {
+ setValidOrgKey = (almOrganization: AlmOrganization) => {
const key = slugify(almOrganization.key);
const keys = [key, ...times(9, i => `${key}-${i + 1}`)];
return api
@@ -167,24 +171,31 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr
() => key
)
.then(key => {
- return { ...almOrganization, key };
+ return { almOrganization: { ...almOrganization, key } };
});
};
fetchAlmOrganization = (installationId: string) => {
this.setState({ almOrgLoading: true });
return getAlmOrganization({ installationId })
- .then(this.fetchValidOrgKey)
- .then(almOrganization => {
- if (this.mounted) {
- this.setState({ almOrganization, almOrgLoading: false });
+ .then(({ almOrganization, boundOrganization }) => {
+ if (boundOrganization) {
+ return Promise.resolve({ almOrganization, boundOrganization });
}
+ return this.setValidOrgKey(almOrganization);
})
- .catch(() => {
- if (this.mounted) {
- this.setState({ almOrgLoading: false });
+ .then(
+ ({ almOrganization, boundOrganization }: GetAlmOrganizationResponse) => {
+ if (this.mounted) {
+ this.setState({ almOrganization, almOrgLoading: false, boundOrganization });
+ }
+ },
+ () => {
+ if (this.mounted) {
+ this.setState({ almOrgLoading: false });
+ }
}
- });
+ );
};
fetchSubscriptionPlans = () => {
@@ -196,8 +207,7 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr
};
handleOrgCreated = (organization: string, justCreated = true) => {
- skipOnboarding().catch(() => {});
- this.props.skipOnboardingAction();
+ this.props.skipOnboarding();
const redirectProjectTimestamp = get(ORGANIZATION_IMPORT_REDIRECT_TO_PROJECT_TIMESTAMP);
remove(ORGANIZATION_IMPORT_REDIRECT_TO_PROJECT_TIMESTAMP);
if (
@@ -237,7 +247,7 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr
renderContent = (almInstallId?: string, importPersonalOrg?: Organization) => {
const { currentUser, location } = this.props;
const { almApplication, almOrganization } = this.state;
- const state = (location.state || {}) as LocationState;
+ const state: LocationState = location.state || {};
if (importPersonalOrg && almOrganization && almApplication) {
return (
@@ -287,6 +297,7 @@ export class CreateOrganization extends React.PureComponent<Props & WithRouterPr
almInstallId={almInstallId}
almOrganization={almOrganization}
almUnboundApplications={this.state.almUnboundApplications}
+ boundOrganization={this.state.boundOrganization}
createOrganization={this.props.createOrganization}
onOrgCreated={this.handleOrgCreated}
unboundOrganizations={this.props.userOrganizations.filter(
@@ -392,7 +403,7 @@ const mapDispatchToProps = {
createOrganization: createOrganization as any,
deleteOrganization: deleteOrganization as any,
updateOrganization: updateOrganization as any,
- skipOnboardingAction: skipOnboardingAction as any
+ skipOnboarding: skipOnboarding as any
};
export default whenLoggedIn(
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/ChooseRemoteOrganizationStep-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/ChooseRemoteOrganizationStep-test.tsx
index c9fa537806c..40d80e0b4bb 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/ChooseRemoteOrganizationStep-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/ChooseRemoteOrganizationStep-test.tsx
@@ -31,7 +31,7 @@ it('should display an alert message', () => {
});
it('should display unbound installations', () => {
- const installation = { installationId: '12345', name: 'Foo' };
+ const installation = { installationId: '12345', key: 'foo', name: 'Foo' };
const push = jest.fn();
const wrapper = shallowRender({
almUnboundApplications: [installation],
@@ -47,6 +47,16 @@ it('should display unbound installations', () => {
});
});
+it('should display already bound alert message', () => {
+ expect(
+ shallowRender({
+ almInstallId: 'foo',
+ almOrganization: { avatar: 'foo-avatar', key: 'foo', name: 'Foo', personal: false },
+ boundOrganization: { avatar: 'bound-avatar', key: 'bound', name: 'Bound' }
+ }).find('Alert')
+ ).toMatchSnapshot();
+});
+
function shallowRender(props: Partial<ChooseRemoteOrganizationStep['props']> = {}) {
return shallow(
// @ts-ignore avoid passing everything from WithRouterProps
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx b/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx
index 38e0c5bb65b..1c505aab71d 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/CreateOrganization-test.tsx
@@ -50,14 +50,16 @@ jest.mock('../../../../api/alm-integration', () => ({
}
}),
getAlmOrganization: jest.fn().mockResolvedValue({
- avatar: 'my-avatar',
- description: 'Continuous Code Quality',
- key: 'sonarsource',
- name: 'SonarSource',
- personal: false,
- url: 'https://www.sonarsource.com'
+ almOrganization: {
+ avatar: 'my-avatar',
+ description: 'Continuous Code Quality',
+ key: 'sonarsource',
+ name: 'SonarSource',
+ personal: false,
+ url: 'https://www.sonarsource.com'
+ }
}),
- listUnboundApplications: jest.fn().mockResolvedValue({ applications: [] })
+ listUnboundApplications: jest.fn().mockResolvedValue([])
}));
jest.mock('../../../../api/organizations', () => ({
@@ -127,10 +129,12 @@ it('should render with auto tab selected and manual disabled', async () => {
it('should render with auto personal organization bind page', async () => {
(getAlmOrganization as jest.Mock<any>).mockResolvedValueOnce({
- key: 'foo',
- name: 'Foo',
- avatar: 'my-avatar',
- personal: true
+ almOrganization: {
+ key: 'foo',
+ name: 'Foo',
+ avatar: 'my-avatar',
+ personal: true
+ }
});
const wrapper = shallowRender({
currentUser: { ...user, externalProvider: 'github', personalOrganization: 'foo' },
@@ -143,10 +147,12 @@ it('should render with auto personal organization bind page', async () => {
it('should slugify and find a uniq organization key', async () => {
(getAlmOrganization as jest.Mock<any>).mockResolvedValueOnce({
- avatar: 'https://avatars3.githubusercontent.com/u/37629810?v=4',
- key: 'Foo&Bar',
- name: 'Foo & Bar',
- personal: true
+ almOrganization: {
+ avatar: 'https://avatars3.githubusercontent.com/u/37629810?v=4',
+ key: 'Foo&Bar',
+ name: 'Foo & Bar',
+ personal: true
+ }
});
(getOrganizations as jest.Mock<any>).mockResolvedValueOnce({
organizations: [{ key: 'foo-and-bar' }, { key: 'foo-and-bar-1' }]
@@ -246,7 +252,7 @@ function shallowRender(props: Partial<CreateOrganization['props']> = {}) {
// @ts-ignore avoid passing everything from WithRouterProps
location={{}}
router={mockRouter()}
- skipOnboardingAction={jest.fn()}
+ skipOnboarding={jest.fn()}
updateOrganization={jest.fn()}
userOrganizations={[
{ actions: { admin: true }, key: 'foo', name: 'Foo' },
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoPersonalOrganizationBind-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoPersonalOrganizationBind-test.tsx.snap
index f29f5f9d008..2b2026ad813 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoPersonalOrganizationBind-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/AutoPersonalOrganizationBind-test.tsx.snap
@@ -12,7 +12,7 @@ exports[`should render correctly 1`] = `
}
}
>
- <p
+ <div
className="huge-spacer-bottom"
>
<FormattedMessage
@@ -44,7 +44,7 @@ exports[`should render correctly 1`] = `
}
}
/>
- </p>
+ </div>
<OrganizationDetailsForm
keyReadOnly={true}
onContinue={[Function]}
diff --git a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ChooseRemoteOrganizationStep-test.tsx.snap b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ChooseRemoteOrganizationStep-test.tsx.snap
index 77778ab5130..6faa756f708 100644
--- a/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ChooseRemoteOrganizationStep-test.tsx.snap
+++ b/server/sonar-web/src/main/js/apps/create/organization/__tests__/__snapshots__/ChooseRemoteOrganizationStep-test.tsx.snap
@@ -1,19 +1,62 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
+exports[`should display already bound alert message 1`] = `
+<Alert
+ className="big-spacer-bottom width-60"
+ variant="error"
+>
+ <FormattedMessage
+ defaultMessage="onboarding.import_organization.already_bound_x"
+ id="onboarding.import_organization.already_bound_x"
+ values={
+ Object {
+ "avatar": <img
+ alt="GitHub"
+ className="little-spacer-left"
+ src="/images/sonarcloud/github.svg"
+ width={16}
+ />,
+ "boundAvatar": <OrganizationAvatar
+ className="little-spacer-left"
+ organization={
+ Object {
+ "avatar": "bound-avatar",
+ "key": "bound",
+ "name": "Bound",
+ }
+ }
+ small={true}
+ />,
+ "boundName": <strong>
+ Bound
+ </strong>,
+ "name": <strong>
+ Foo
+ </strong>,
+ }
+ }
+ />
+</Alert>
+`;
+
exports[`should display an alert message 1`] = `
<Alert
- className="markdown big-spacer-bottom width-60"
+ className="big-spacer-bottom width-60"
variant="error"
>
- onboarding.import_organization.org_not_found
- <ul>
- <li>
- onboarding.import_organization.org_not_found.tips_1
- </li>
- <li>
- onboarding.import_organization.org_not_found.tips_2
- </li>
- </ul>
+ <div
+ className="markdown"
+ >
+ onboarding.import_organization.org_not_found
+ <ul>
+ <li>
+ onboarding.import_organization.org_not_found.tips_1
+ </li>
+ <li>
+ onboarding.import_organization.org_not_found.tips_2
+ </li>
+ </ul>
+ </div>
</Alert>
`;
@@ -103,6 +146,7 @@ exports[`should display unbound installations 1`] = `
Array [
Object {
"installationId": "12345",
+ "key": "foo",
"name": "Foo",
},
]