]> source.dussan.org Git - sonarqube.git/commitdiff
Improve user profile & unit tests
authorPhilippe Perrin <philippe.perrin@sonarsource.com>
Fri, 11 Oct 2019 07:32:45 +0000 (09:32 +0200)
committerSonarTech <sonartech@sonarsource.com>
Wed, 16 Oct 2019 09:09:14 +0000 (11:09 +0200)
23 files changed:
server/sonar-web/src/main/js/app/components/AccountDeleted.tsx [deleted file]
server/sonar-web/src/main/js/app/components/__tests__/AccountDeleted-test.tsx [deleted file]
server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/AccountDeleted-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/app/utils/startReactApp.tsx
server/sonar-web/src/main/js/apps/account/account.css
server/sonar-web/src/main/js/apps/account/profile/Profile.tsx
server/sonar-web/src/main/js/apps/account/profile/UserDeleteAccount.tsx [deleted file]
server/sonar-web/src/main/js/apps/account/profile/UserDeleteAccountContent.tsx [deleted file]
server/sonar-web/src/main/js/apps/account/profile/UserDeleteAccountModal.tsx [deleted file]
server/sonar-web/src/main/js/apps/account/profile/UserExternalIdentity.tsx
server/sonar-web/src/main/js/apps/account/profile/UserGroups.tsx [deleted file]
server/sonar-web/src/main/js/apps/account/profile/UserScmAccounts.tsx [deleted file]
server/sonar-web/src/main/js/apps/account/profile/__tests__/Profile-test.tsx
server/sonar-web/src/main/js/apps/account/profile/__tests__/UserDeleteAccount-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/account/profile/__tests__/UserDeleteAccountContent-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/account/profile/__tests__/UserDeleteAccountModal-test.tsx [deleted file]
server/sonar-web/src/main/js/apps/account/profile/__tests__/UserExternalIdentity-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/Profile-test.tsx.snap
server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/UserDeleteAccount-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/UserDeleteAccountContent-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/UserDeleteAccountModal-test.tsx.snap [deleted file]
server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/UserExternalIdentity-test.tsx.snap [new file with mode: 0644]
sonar-core/src/main/resources/org/sonar/l10n/core.properties

diff --git a/server/sonar-web/src/main/js/app/components/AccountDeleted.tsx b/server/sonar-web/src/main/js/app/components/AccountDeleted.tsx
deleted file mode 100644 (file)
index d91059b..0000000
+++ /dev/null
@@ -1,53 +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 { FormattedMessage } from 'react-intl';
-import { Link } from 'react-router';
-import { Alert } from 'sonar-ui-common/components/ui/Alert';
-import { translate } from 'sonar-ui-common/helpers/l10n';
-
-export default function AccountDeleted() {
-  return (
-    <div className="page-wrapper-simple display-flex-column">
-      <Alert className="huge-spacer-bottom" variant="success">
-        {translate('my_profile.delete_account.success')}
-      </Alert>
-
-      <div className="page-simple text-center">
-        <p className="big-spacer-bottom">
-          <h1>{translate('my_profile.delete_account.feedback.reason.explanation')}</h1>
-        </p>
-        <p className="spacer-bottom">
-          <FormattedMessage
-            defaultMessage={translate('my_profile.delete_account.feedback.call_to_action')}
-            id="my_profile.delete_account.feedback.call_to_action"
-            values={{
-              link: <Link to="/about/contact">{translate('footer.contact_us')}</Link>
-            }}
-          />
-        </p>
-        <p>
-          <Link to="/">{translate('go_back_to_homepage')}</Link>
-        </p>
-      </div>
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/app/components/__tests__/AccountDeleted-test.tsx b/server/sonar-web/src/main/js/app/components/__tests__/AccountDeleted-test.tsx
deleted file mode 100644 (file)
index 608b22d..0000000
+++ /dev/null
@@ -1,27 +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 { shallow } from 'enzyme';
-import * as React from 'react';
-import AccountDeleted from '../AccountDeleted';
-
-it('should render correctly', () => {
-  expect(shallow(<AccountDeleted />)).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/AccountDeleted-test.tsx.snap b/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/AccountDeleted-test.tsx.snap
deleted file mode 100644 (file)
index 8bffcc4..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<div
-  className="page-wrapper-simple display-flex-column"
->
-  <Alert
-    className="huge-spacer-bottom"
-    variant="success"
-  >
-    my_profile.delete_account.success
-  </Alert>
-  <div
-    className="page-simple text-center"
-  >
-    <p
-      className="big-spacer-bottom"
-    >
-      <h1>
-        my_profile.delete_account.feedback.reason.explanation
-      </h1>
-    </p>
-    <p
-      className="spacer-bottom"
-    >
-      <FormattedMessage
-        defaultMessage="my_profile.delete_account.feedback.call_to_action"
-        id="my_profile.delete_account.feedback.call_to_action"
-        values={
-          Object {
-            "link": <Link
-              onlyActiveOnIndex={false}
-              style={Object {}}
-              to="/about/contact"
-            >
-              footer.contact_us
-            </Link>,
-          }
-        }
-      />
-    </p>
-    <p>
-      <Link
-        onlyActiveOnIndex={false}
-        style={Object {}}
-        to="/"
-      >
-        go_back_to_homepage
-      </Link>
-    </p>
-  </div>
-</div>
-`;
index 1eb7843fbcc3f476cffba82874b503abfa0ffa9a..0e91d2c37fbc382e1de9f9e94cefde39bfc252ac 100644 (file)
@@ -207,10 +207,6 @@ export default function startReactApp(
                           import('../../apps/feedback/downgrade/DowngradeFeedback')
                         )}
                       />
-                      <Route
-                        path="account-deleted"
-                        component={lazyLoad(() => import('../components/AccountDeleted'))}
-                      />
                     </>
                   )}
                   <RouteWithChildRoutes path="organizations" childRoutes={organizationsRoutes} />
index eab942f9e5e4c113273b726bcd608f707c6ef4b7..f7575bfc71495c849b951f1ef7905e4cfe119475 100644 (file)
@@ -59,9 +59,7 @@
   padding: 40px 0;
 }
 
-.account-separator {
-  height: 0;
-  margin: 40px 0;
+.account-profile .boxed-group-inner:not(:first-child) {
   border-top: 1px solid var(--barBorderColor);
 }
 
index 4b55078de410e6a66484d4e31fafa1a529106220..a05a7d04302cfcee76b71e0bf101487753a1ba9c 100644 (file)
  * 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';
 import { whenLoggedIn } from '../../../components/hoc/whenLoggedIn';
-import { isSonarCloud } from '../../../helpers/system';
-import UserDeleteAccount from './UserDeleteAccount';
 import UserExternalIdentity from './UserExternalIdentity';
-import UserGroups from './UserGroups';
-import UserScmAccounts from './UserScmAccounts';
 
-export interface Props {
+export interface ProfileProps {
   currentUser: T.LoggedInUser;
 }
 
-export function Profile({ currentUser }: Props) {
+export function Profile({ currentUser }: ProfileProps) {
+  const isExternalProvider = !currentUser.local && currentUser.externalProvider !== 'sonarqube';
+
   return (
-    <div className="account-body account-container">
-      <div className="boxed-group boxed-group-inner">
-        <div className="spacer-bottom">
-          {translate('login')}: <strong id="login">{currentUser.login}</strong>
-        </div>
+    <div className="account-body account-container account-profile">
+      <div className="boxed-group">
+        {renderLogin()}
+        {renderEmail()}
+        {renderUserGroups()}
+        {renderScmAccounts()}
+      </div>
+    </div>
+  );
 
-        {!currentUser.local && currentUser.externalProvider !== 'sonarqube' && (
-          <div className="spacer-bottom" id="identity-provider">
-            <UserExternalIdentity user={currentUser} />
-          </div>
-        )}
+  function renderLogin() {
+    if (!currentUser.login && !isExternalProvider) {
+      return null;
+    }
 
-        {Boolean(currentUser.email) && (
-          <div className="spacer-bottom">
-            {translate('my_profile.email')}: <strong id="email">{currentUser.email}</strong>
-          </div>
+    return (
+      <div className="boxed-group-inner">
+        <h2 className="spacer-bottom">{translate('my_profile.login')}</h2>
+        {currentUser.login && (
+          <p className="spacer-bottom" id="login">
+            {currentUser.login}
+          </p>
         )}
+        {isExternalProvider && <UserExternalIdentity user={currentUser} />}
+      </div>
+    );
+  }
 
-        {!isSonarCloud() && (
-          <>
-            <hr className="account-separator" />
-            <UserGroups groups={currentUser.groups} />
-          </>
-        )}
+  function renderEmail() {
+    if (!currentUser.email) {
+      return null;
+    }
 
-        <hr />
+    return (
+      <div className="boxed-group-inner">
+        <h2 className="spacer-bottom">{translate('my_profile.email')}</h2>
+        <div className="spacer-bottom">
+          <p id="email">{currentUser.email}</p>
+        </div>
+      </div>
+    );
+  }
 
-        <UserScmAccounts scmAccounts={currentUser.scmAccounts} user={currentUser} />
+  function renderUserGroups() {
+    if (!currentUser.groups || currentUser.groups.length === 0) {
+      return null;
+    }
 
-        {isSonarCloud() && (
-          <>
-            <hr />
+    return (
+      <div className="boxed-group-inner">
+        <h2 className="spacer-bottom">{translate('my_profile.groups')}</h2>
+        <ul id="groups">
+          {currentUser.groups.map(group => (
+            <li className="little-spacer-bottom" key={group} title={group}>
+              {group}
+            </li>
+          ))}
+        </ul>
+      </div>
+    );
+  }
 
-            <UserDeleteAccount user={currentUser} />
-          </>
-        )}
+  function renderScmAccounts() {
+    if (
+      !currentUser.login &&
+      !currentUser.email &&
+      (!currentUser.scmAccounts || currentUser.scmAccounts.length === 0)
+    ) {
+      return null;
+    }
+
+    return (
+      <div className="boxed-group-inner">
+        <h2 className="spacer-bottom">
+          {translate('my_profile.scm_accounts')}
+          <HelpTooltip
+            className="little-spacer-left"
+            overlay={translate('my_profile.scm_accounts.tooltip')}
+          />
+        </h2>
+        <ul id="scm-accounts">
+          {currentUser.login && (
+            <li className="little-spacer-bottom text-ellipsis" title={currentUser.login}>
+              {currentUser.login}
+            </li>
+          )}
+
+          {currentUser.email && (
+            <li className="little-spacer-bottom text-ellipsis" title={currentUser.email}>
+              {currentUser.email}
+            </li>
+          )}
+
+          {currentUser.scmAccounts &&
+            currentUser.scmAccounts.length > 0 &&
+            currentUser.scmAccounts.map(scmAccount => (
+              <li className="little-spacer-bottom" key={scmAccount} title={scmAccount}>
+                {scmAccount}
+              </li>
+            ))}
+        </ul>
       </div>
-    </div>
-  );
+    );
+  }
 }
 
 export default whenLoggedIn(Profile);
diff --git a/server/sonar-web/src/main/js/apps/account/profile/UserDeleteAccount.tsx b/server/sonar-web/src/main/js/apps/account/profile/UserDeleteAccount.tsx
deleted file mode 100644 (file)
index d114dc9..0000000
+++ /dev/null
@@ -1,125 +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 { Button } from 'sonar-ui-common/components/controls/buttons';
-import DeferredSpinner from 'sonar-ui-common/components/ui/DeferredSpinner';
-import { translate } from 'sonar-ui-common/helpers/l10n';
-import { getOrganizationsThatPreventDeletion } from '../../../api/organizations';
-import { whenLoggedIn } from '../../../components/hoc/whenLoggedIn';
-import { withUserOrganizations } from '../../../components/hoc/withUserOrganizations';
-import UserDeleteAccountContent from './UserDeleteAccountContent';
-import UserDeleteAccountModal from './UserDeleteAccountModal';
-
-interface Props {
-  user: T.LoggedInUser;
-  userOrganizations: T.Organization[];
-}
-
-interface State {
-  loading: boolean;
-  organizationsToTransferOrDelete: T.Organization[];
-  showModal: boolean;
-}
-
-export class UserDeleteAccount extends React.PureComponent<Props, State> {
-  mounted = false;
-
-  state: State = {
-    loading: true,
-    organizationsToTransferOrDelete: [],
-    showModal: false
-  };
-
-  componentDidMount() {
-    this.mounted = true;
-    this.fetchOrganizationsThatPreventDeletion();
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-  }
-
-  fetchOrganizationsThatPreventDeletion = () => {
-    getOrganizationsThatPreventDeletion().then(
-      ({ organizations }) => {
-        if (this.mounted) {
-          this.setState({
-            loading: false,
-            organizationsToTransferOrDelete: organizations
-          });
-        }
-      },
-      () => {}
-    );
-  };
-
-  toggleModal = () => {
-    if (this.mounted) {
-      this.setState(state => ({
-        showModal: !state.showModal
-      }));
-    }
-  };
-
-  render() {
-    const { user, userOrganizations } = this.props;
-    const { organizationsToTransferOrDelete, loading, showModal } = this.state;
-
-    const label = translate('my_profile.delete_account');
-
-    return (
-      <div>
-        <h2 className="spacer-bottom">{label}</h2>
-
-        <DeferredSpinner loading={loading} />
-
-        {!loading && (
-          <>
-            <UserDeleteAccountContent
-              className="list-styled no-padding big-spacer-top big-spacer-bottom"
-              organizationsSafeToDelete={userOrganizations}
-              organizationsToTransferOrDelete={organizationsToTransferOrDelete}
-            />
-
-            <Button
-              className="button-red"
-              disabled={organizationsToTransferOrDelete.length > 0}
-              onClick={this.toggleModal}
-              type="button">
-              {translate('delete')}
-            </Button>
-
-            {showModal && (
-              <UserDeleteAccountModal
-                label={label}
-                organizationsSafeToDelete={userOrganizations}
-                organizationsToTransferOrDelete={organizationsToTransferOrDelete}
-                toggleModal={this.toggleModal}
-                user={user}
-              />
-            )}
-          </>
-        )}
-      </div>
-    );
-  }
-}
-
-export default whenLoggedIn(withUserOrganizations(UserDeleteAccount));
diff --git a/server/sonar-web/src/main/js/apps/account/profile/UserDeleteAccountContent.tsx b/server/sonar-web/src/main/js/apps/account/profile/UserDeleteAccountContent.tsx
deleted file mode 100644 (file)
index aaad598..0000000
+++ /dev/null
@@ -1,148 +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 { FormattedMessage } from 'react-intl';
-import { Link } from 'react-router';
-import { Alert } from 'sonar-ui-common/components/ui/Alert';
-import { translate } from 'sonar-ui-common/helpers/l10n';
-import { getOrganizationUrl } from '../../../helpers/urls';
-
-function getOrganizationLink(org: T.Organization, i: number, organizations: T.Organization[]) {
-  return (
-    <span key={org.key}>
-      <Link to={getOrganizationUrl(org.key)}>{org.name}</Link>
-      {i < organizations.length - 1 && ', '}
-    </span>
-  );
-}
-
-export function ShowOrganizationsToTransferOrDelete({
-  organizations
-}: {
-  organizations: T.Organization[];
-}) {
-  return (
-    <>
-      <p className="big-spacer-bottom">
-        <FormattedMessage
-          defaultMessage={translate('my_profile.delete_account.info.orgs_to_transfer_or_delete')}
-          id="my_profile.delete_account.info.orgs_to_transfer_or_delete"
-          values={{
-            organizations: <>{organizations.map(getOrganizationLink)}</>
-          }}
-        />
-      </p>
-
-      <Alert className="big-spacer-bottom" variant="warning">
-        <FormattedMessage
-          defaultMessage={translate(
-            'my_profile.delete_account.info.orgs_to_transfer_or_delete.info'
-          )}
-          id="my_profile.delete_account.info.orgs_to_transfer_or_delete.info"
-          values={{
-            link: (
-              <a
-                href="https://sieg.eu.ngrok.io/documentation/organizations/overview/#how-to-transfer-ownership-of-an-organization"
-                rel="noopener noreferrer"
-                target="_blank">
-                {translate('my_profile.delete_account.info.orgs_to_transfer_or_delete.info.link')}
-              </a>
-            )
-          }}
-        />
-      </Alert>
-    </>
-  );
-}
-
-export function ShowOrganizations({
-  className,
-  organizations
-}: {
-  className?: string;
-  organizations: T.Organization[];
-}) {
-  const organizationsIAdministrate = organizations.filter(o => o.actions && o.actions.admin);
-
-  return (
-    <ul className={className}>
-      <li className="spacer-bottom">{translate('my_profile.delete_account.info')}</li>
-
-      <li className="spacer-bottom">
-        <FormattedMessage
-          defaultMessage={translate('my_profile.delete_account.data.info')}
-          id="my_profile.delete_account.data.info"
-          values={{
-            help: (
-              <a
-                href="/documentation/user-guide/user-account/#delete-your-user-account"
-                rel="noopener noreferrer"
-                target="_blank">
-                {translate('learn_more')}
-              </a>
-            )
-          }}
-        />
-      </li>
-
-      {organizations.length > 0 && (
-        <li className="spacer-bottom">
-          <FormattedMessage
-            defaultMessage={translate('my_profile.delete_account.info.orgs.members')}
-            id="my_profile.delete_account.info.orgs.members"
-            values={{
-              organizations: <>{organizations.map(getOrganizationLink)}</>
-            }}
-          />
-        </li>
-      )}
-
-      {organizationsIAdministrate.length > 0 && (
-        <li className="spacer-bottom">
-          <FormattedMessage
-            defaultMessage={translate('my_profile.delete_account.info.orgs.administrators')}
-            id="my_profile.delete_account.info.orgs.administrators"
-            values={{
-              organizations: <>{organizationsIAdministrate.map(getOrganizationLink)}</>
-            }}
-          />
-        </li>
-      )}
-    </ul>
-  );
-}
-
-interface UserDeleteAccountContentProps {
-  className?: string;
-  organizationsSafeToDelete: T.Organization[];
-  organizationsToTransferOrDelete: T.Organization[];
-}
-
-export default function UserDeleteAccountContent({
-  className,
-  organizationsSafeToDelete,
-  organizationsToTransferOrDelete
-}: UserDeleteAccountContentProps) {
-  if (organizationsToTransferOrDelete.length > 0) {
-    return <ShowOrganizationsToTransferOrDelete organizations={organizationsToTransferOrDelete} />;
-  }
-
-  return <ShowOrganizations className={className} organizations={organizationsSafeToDelete} />;
-}
diff --git a/server/sonar-web/src/main/js/apps/account/profile/UserDeleteAccountModal.tsx b/server/sonar-web/src/main/js/apps/account/profile/UserDeleteAccountModal.tsx
deleted file mode 100644 (file)
index 80ea464..0000000
+++ /dev/null
@@ -1,141 +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 { connect } from 'react-redux';
-import InputValidationField from 'sonar-ui-common/components/controls/InputValidationField';
-import ValidationModal from 'sonar-ui-common/components/controls/ValidationModal';
-import { Alert } from 'sonar-ui-common/components/ui/Alert';
-import { translate, translateWithParameters } from 'sonar-ui-common/helpers/l10n';
-import { deactivateUser } from '../../../api/users';
-import RecentHistory from '../../../app/components/RecentHistory';
-import { Router, withRouter } from '../../../components/hoc/withRouter';
-import { doLogout } from '../../../store/rootActions';
-import UserDeleteAccountContent from './UserDeleteAccountContent';
-
-interface Values {
-  login: string;
-}
-
-interface DeleteModalProps {
-  doLogout: () => Promise<void>;
-  label: string;
-  organizationsSafeToDelete: T.Organization[];
-  organizationsToTransferOrDelete: T.Organization[];
-  router: Pick<Router, 'push'>;
-  toggleModal: VoidFunction;
-  user: T.LoggedInUser;
-}
-
-export class UserDeleteAccountModal extends React.PureComponent<DeleteModalProps> {
-  handleSubmit = () => {
-    const { user } = this.props;
-
-    return deactivateUser({ login: user.login })
-      .then(this.props.doLogout)
-      .then(() => {
-        RecentHistory.clear();
-        window.location.replace('/account-deleted');
-      });
-  };
-
-  handleValidate = ({ login }: Values) => {
-    const { user } = this.props;
-    const errors: { login?: string } = {};
-    const trimmedLogin = login.trim();
-
-    if (!trimmedLogin) {
-      errors.login = translate('my_profile.delete_account.login.required');
-    } else if (user.externalIdentity && trimmedLogin !== user.externalIdentity.trim()) {
-      errors.login = translate('my_profile.delete_account.login.wrong_value');
-    }
-
-    return errors;
-  };
-
-  render() {
-    const {
-      label,
-      organizationsSafeToDelete,
-      organizationsToTransferOrDelete,
-      toggleModal,
-      user
-    } = this.props;
-
-    return (
-      <ValidationModal
-        confirmButtonText={translate('delete')}
-        header={translateWithParameters(
-          'my_profile.delete_account.modal.header',
-          label,
-          user.externalIdentity || ''
-        )}
-        initialValues={{
-          login: ''
-        }}
-        isDestructive={true}
-        onClose={toggleModal}
-        onSubmit={this.handleSubmit}
-        validate={this.handleValidate}>
-        {({ dirty, errors, handleBlur, handleChange, isSubmitting, touched, values }) => (
-          <>
-            <Alert className="big-spacer-bottom" variant="error">
-              {translate('my_profile.warning_message')}
-            </Alert>
-
-            <UserDeleteAccountContent
-              className="list-styled no-padding big-spacer-bottom"
-              organizationsSafeToDelete={organizationsSafeToDelete}
-              organizationsToTransferOrDelete={organizationsToTransferOrDelete}
-            />
-
-            <InputValidationField
-              autoFocus={true}
-              dirty={dirty}
-              disabled={isSubmitting}
-              error={errors.login}
-              id="user-login"
-              label={
-                <label htmlFor="user-login">
-                  {translate('my_profile.delete_account.verify')}
-                  <em className="mandatory">*</em>
-                </label>
-              }
-              name="login"
-              onBlur={handleBlur}
-              onChange={handleChange}
-              touched={touched.login}
-              type="text"
-              value={values.login}
-            />
-          </>
-        )}
-      </ValidationModal>
-    );
-  }
-}
-
-const mapStateToProps = () => ({});
-
-const mapDispatchToProps = { doLogout: doLogout as any };
-
-export default connect(
-  mapStateToProps,
-  mapDispatchToProps
-)(withRouter(UserDeleteAccountModal));
index 523529ae9ca7652a25f56ab35747c38bd89d7437..a4add5f55e17fa7ab37d07d5ed42c2a4b4da8b0a 100644 (file)
@@ -23,7 +23,7 @@ import { getBaseUrl } from 'sonar-ui-common/helpers/urls';
 import { getIdentityProviders } from '../../../api/users';
 import { colors } from '../../../app/theme';
 
-interface Props {
+export interface UserExternalIdentityProps {
   user: T.LoggedInUser;
 }
 
@@ -32,7 +32,10 @@ interface State {
   loading: boolean;
 }
 
-export default class UserExternalIdentity extends React.PureComponent<Props, State> {
+export default class UserExternalIdentity extends React.PureComponent<
+  UserExternalIdentityProps,
+  State
+> {
   mounted = false;
   state: State = {
     loading: true
@@ -43,7 +46,7 @@ export default class UserExternalIdentity extends React.PureComponent<Props, Sta
     this.fetchIdentityProviders();
   }
 
-  componentDidUpdate(prevProps: Props) {
+  componentDidUpdate(prevProps: UserExternalIdentityProps) {
     if (prevProps.user !== this.props.user) {
       this.fetchIdentityProviders();
     }
diff --git a/server/sonar-web/src/main/js/apps/account/profile/UserGroups.tsx b/server/sonar-web/src/main/js/apps/account/profile/UserGroups.tsx
deleted file mode 100644 (file)
index a818818..0000000
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2019 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
- */
-import * as React from 'react';
-import { translate } from 'sonar-ui-common/helpers/l10n';
-
-interface Props {
-  groups: string[];
-}
-
-export default function UserGroups({ groups }: Props) {
-  return (
-    <div>
-      <h2 className="spacer-bottom">{translate('my_profile.groups')}</h2>
-      <ul id="groups">
-        {groups.map(group => (
-          <li className="little-spacer-bottom" key={group} title={group}>
-            {group}
-          </li>
-        ))}
-      </ul>
-    </div>
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/account/profile/UserScmAccounts.tsx b/server/sonar-web/src/main/js/apps/account/profile/UserScmAccounts.tsx
deleted file mode 100644 (file)
index 9055fc5..0000000
+++ /dev/null
@@ -1,58 +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 HelpTooltip from 'sonar-ui-common/components/controls/HelpTooltip';
-import { translate } from 'sonar-ui-common/helpers/l10n';
-
-interface Props {
-  scmAccounts: string[];
-  user: T.LoggedInUser;
-}
-
-export default function UserScmAccounts({ user, scmAccounts }: Props) {
-  return (
-    <div>
-      <h2 className="spacer-bottom">
-        {translate('my_profile.scm_accounts')}
-        <HelpTooltip
-          className="little-spacer-left"
-          overlay={translate('my_profile.scm_accounts.tooltip')}
-        />
-      </h2>
-      <ul id="scm-accounts">
-        <li className="little-spacer-bottom text-ellipsis" title={user.login}>
-          {user.login}
-        </li>
-
-        {user.email && (
-          <li className="little-spacer-bottom text-ellipsis" title={user.email}>
-            {user.email}
-          </li>
-        )}
-
-        {scmAccounts.map(scmAccount => (
-          <li className="little-spacer-bottom" key={scmAccount} title={scmAccount}>
-            {scmAccount}
-          </li>
-        ))}
-      </ul>
-    </div>
-  );
-}
index 479effa96fbd3b4f44c7f89219104b9983e60e72..175fada451d062c120a5939b39b14e2a4a3bebcb 100644 (file)
  */
 import { shallow } from 'enzyme';
 import * as React from 'react';
-import { isSonarCloud } from '../../../../helpers/system';
 import { mockLoggedInUser } from '../../../../helpers/testMocks';
-import { Profile, Props } from '../Profile';
+import { Profile, ProfileProps } from '../Profile';
 
-jest.mock('../../../../helpers/system', () => ({ isSonarCloud: jest.fn().mockReturnValue(false) }));
-
-it('should render correctly', () => {
-  expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should render email', () => {
-  expect(
-    shallowRender(mockLoggedInUser({ email: 'john@doe.com' }))
-      .find('#email')
-      .exists()
-  ).toBe(true);
-});
-
-it('should render external identity', () => {
-  expect(
-    shallowRender(mockLoggedInUser({ local: false, externalProvider: 'github' }))
-      .find('UserExternalIdentity')
-      .exists()
-  ).toBe(true);
+it('should render correctly a local user', () => {
+  expect(shallowRender({ local: true, externalProvider: 'sonarqube' })).toMatchSnapshot();
 });
 
-it('should not display user groups', () => {
-  (isSonarCloud as jest.Mock).mockReturnValueOnce(true);
+it('should render correctly a IDP user', () => {
   expect(
-    shallowRender()
-      .find('UserGroups')
-      .exists()
-  ).toBe(false);
+    shallowRender({
+      local: false,
+      externalProvider: 'github',
+      email: undefined,
+      login: undefined,
+      scmAccounts: []
+    })
+  ).toMatchSnapshot();
 });
 
-function shallowRender(currentUser: Props['currentUser'] = mockLoggedInUser()) {
-  return shallow(<Profile currentUser={currentUser} />);
+function shallowRender(userOverrides?: Partial<ProfileProps['currentUser']>) {
+  return shallow(
+    <Profile
+      currentUser={{
+        ...mockLoggedInUser({
+          email: 'john@doe.com',
+          groups: ['G1', 'G2'],
+          scmAccounts: ['SCM1', 'SCM2'],
+          ...userOverrides
+        })
+      }}
+    />
+  );
 }
diff --git a/server/sonar-web/src/main/js/apps/account/profile/__tests__/UserDeleteAccount-test.tsx b/server/sonar-web/src/main/js/apps/account/profile/__tests__/UserDeleteAccount-test.tsx
deleted file mode 100644 (file)
index 215e94f..0000000
+++ /dev/null
@@ -1,87 +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 { shallow } from 'enzyme';
-import * as React from 'react';
-import { click, waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
-import { getOrganizationsThatPreventDeletion } from '../../../../api/organizations';
-import { mockLoggedInUser, mockOrganization } from '../../../../helpers/testMocks';
-import { UserDeleteAccount } from '../UserDeleteAccount';
-
-jest.mock('../../../../api/organizations', () => ({
-  getOrganizationsThatPreventDeletion: jest.fn().mockResolvedValue({ organizations: [] })
-}));
-
-beforeEach(() => {
-  jest.clearAllMocks();
-});
-
-const organizationToTransferOrDelete = {
-  key: 'luke-leia',
-  name: 'Luke and Leia'
-};
-
-it('should render correctly', async () => {
-  const wrapper = shallowRender();
-  expect(wrapper).toMatchSnapshot();
-
-  await waitAndUpdate(wrapper);
-  expect(wrapper).toMatchSnapshot();
-
-  click(wrapper.find('Button'));
-  expect(wrapper).toMatchSnapshot();
-});
-
-it('should get some organizations', async () => {
-  (getOrganizationsThatPreventDeletion as jest.Mock).mockResolvedValue({
-    organizations: [organizationToTransferOrDelete]
-  });
-
-  const wrapper = shallowRender();
-
-  await waitAndUpdate(wrapper);
-
-  expect(wrapper.state('loading')).toBeFalsy();
-  expect(wrapper.state('organizationsToTransferOrDelete')).toEqual([
-    organizationToTransferOrDelete
-  ]);
-  expect(getOrganizationsThatPreventDeletion).toBeCalled();
-  expect(wrapper.find('Button').prop('disabled')).toBe(true);
-});
-
-it('should toggle modal', () => {
-  const wrapper = shallowRender();
-  wrapper.setState({ loading: false });
-  expect(wrapper.find('Connect(withRouter(UserDeleteAccountModal))').exists()).toBe(false);
-  click(wrapper.find('Button'));
-  expect(wrapper.find('Connect(withRouter(UserDeleteAccountModal))').exists()).toBe(true);
-});
-
-function shallowRender(props: Partial<UserDeleteAccount['props']> = {}) {
-  const user = mockLoggedInUser({ externalIdentity: 'luke' });
-
-  const userOrganizations = [
-    mockOrganization({ key: 'luke-leia', name: 'Luke and Leia' }),
-    mockOrganization({ key: 'luke', name: 'Luke Skywalker' })
-  ];
-
-  return shallow<UserDeleteAccount>(
-    <UserDeleteAccount user={user} userOrganizations={userOrganizations} {...props} />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/account/profile/__tests__/UserDeleteAccountContent-test.tsx b/server/sonar-web/src/main/js/apps/account/profile/__tests__/UserDeleteAccountContent-test.tsx
deleted file mode 100644 (file)
index 37aa8ec..0000000
+++ /dev/null
@@ -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 { shallow } from 'enzyme';
-import * as React from 'react';
-import UserDeleteAccountContent, {
-  ShowOrganizations,
-  ShowOrganizationsToTransferOrDelete
-} from '../UserDeleteAccountContent';
-
-const organizationSafeToDelete = {
-  key: 'luke',
-  name: 'Luke Skywalker'
-};
-
-const organizationToTransferOrDelete = {
-  key: 'luke-leia',
-  name: 'Luke and Leia'
-};
-
-it('should render content correctly', () => {
-  expect(
-    shallow(
-      <UserDeleteAccountContent
-        className="my-class"
-        organizationsSafeToDelete={[organizationSafeToDelete]}
-        organizationsToTransferOrDelete={[organizationToTransferOrDelete]}
-      />
-    )
-  ).toMatchSnapshot();
-
-  expect(
-    shallow(
-      <UserDeleteAccountContent
-        className="my-class"
-        organizationsSafeToDelete={[organizationSafeToDelete]}
-        organizationsToTransferOrDelete={[]}
-      />
-    )
-  ).toMatchSnapshot();
-});
-
-it('should render correctly ShowOrganizationsToTransferOrDelete', () => {
-  expect(
-    shallow(
-      <ShowOrganizationsToTransferOrDelete organizations={[organizationToTransferOrDelete]} />
-    )
-  ).toMatchSnapshot();
-});
-
-it('should render correctly ShowOrganizations', () => {
-  expect(
-    shallow(<ShowOrganizations organizations={[organizationSafeToDelete]} />)
-  ).toMatchSnapshot();
-});
diff --git a/server/sonar-web/src/main/js/apps/account/profile/__tests__/UserDeleteAccountModal-test.tsx b/server/sonar-web/src/main/js/apps/account/profile/__tests__/UserDeleteAccountModal-test.tsx
deleted file mode 100644 (file)
index 0ad5db2..0000000
+++ /dev/null
@@ -1,86 +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 { shallow } from 'enzyme';
-import * as React from 'react';
-import { waitAndUpdate } from 'sonar-ui-common/helpers/testUtils';
-import { deactivateUser } from '../../../../api/users';
-import { mockLoggedInUser, mockRouter } from '../../../../helpers/testMocks';
-import { UserDeleteAccountModal } from '../UserDeleteAccountModal';
-
-jest.mock('../../../../api/users', () => ({
-  deactivateUser: jest.fn()
-}));
-
-const organizationSafeToDelete = {
-  key: 'luke',
-  name: 'Luke Skywalker'
-};
-
-const organizationToTransferOrDelete = {
-  key: 'luke-leia',
-  name: 'Luke and Leia'
-};
-
-it('should render modal correctly', () => {
-  expect(shallowRender()).toMatchSnapshot();
-});
-
-it('should handle submit', async () => {
-  (deactivateUser as jest.Mock).mockResolvedValue(true);
-  window.location.replace = jest.fn();
-
-  const wrapper = shallowRender();
-  const instance = wrapper.instance();
-
-  instance.handleSubmit();
-  await waitAndUpdate(wrapper);
-
-  expect(deactivateUser).toBeCalled();
-  expect(window.location.replace).toHaveBeenCalledWith('/account-deleted');
-});
-
-it('should validate user input', () => {
-  const wrapper = shallowRender();
-  const instance = wrapper.instance();
-  const { handleValidate } = instance;
-
-  expect(handleValidate({ login: '' }).login).toBe('my_profile.delete_account.login.required');
-  expect(handleValidate({ login: 'abc' }).login).toBe(
-    'my_profile.delete_account.login.wrong_value'
-  );
-  expect(handleValidate({ login: 'luke' }).login).toBeUndefined();
-});
-
-function shallowRender(props: Partial<UserDeleteAccountModal['props']> = {}) {
-  const user = mockLoggedInUser({ externalIdentity: 'luke' });
-
-  return shallow<UserDeleteAccountModal>(
-    <UserDeleteAccountModal
-      doLogout={jest.fn().mockResolvedValue(true)}
-      label="label"
-      organizationsSafeToDelete={[organizationSafeToDelete]}
-      organizationsToTransferOrDelete={[organizationToTransferOrDelete]}
-      router={mockRouter()}
-      toggleModal={jest.fn()}
-      user={user}
-      {...props}
-    />
-  );
-}
diff --git a/server/sonar-web/src/main/js/apps/account/profile/__tests__/UserExternalIdentity-test.tsx b/server/sonar-web/src/main/js/apps/account/profile/__tests__/UserExternalIdentity-test.tsx
new file mode 100644 (file)
index 0000000..bbb6e43
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * 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 { mockLoggedInUser } from '../../../../helpers/testMocks';
+import UserExternalIdentity, { UserExternalIdentityProps } from '../UserExternalIdentity';
+
+jest.mock('../../../../api/users', () => ({
+  getIdentityProviders: jest.fn().mockResolvedValue({
+    identityProviders: [
+      {
+        backgroundColor: '#444444',
+        iconPath: '/images/github.svg',
+        key: 'github',
+        name: 'GitHub'
+      }
+    ]
+  })
+}));
+
+it('should render correctly', async () => {
+  const wrapper = shallowRender();
+  await waitAndUpdate(wrapper);
+  expect(wrapper).toMatchSnapshot();
+});
+
+it('should render a fallback when idp is not listed', async () => {
+  const wrapper = shallowRender({ externalProvider: 'ggithub' });
+  await waitAndUpdate(wrapper);
+  expect(wrapper).toMatchSnapshot();
+});
+
+function shallowRender(userOverrides?: Partial<UserExternalIdentityProps['user']>) {
+  return shallow(
+    <UserExternalIdentity
+      user={{
+        ...mockLoggedInUser({
+          email: 'john@doe.com',
+          externalProvider: 'github',
+          local: false,
+          groups: ['G1', 'G2'],
+          scmAccounts: ['SCM1', 'SCM2'],
+          ...userOverrides
+        })
+      }}
+    />
+  );
+}
index e896064c0352d10d8410412ed4cc42cbda70c283..3612928afed9e499776ec172a867debf384063f4 100644 (file)
 // Jest Snapshot v1, https://goo.gl/fbAQLP
 
-exports[`should render correctly 1`] = `
+exports[`should render correctly a IDP user 1`] = `
 <div
-  className="account-body account-container"
+  className="account-body account-container account-profile"
 >
   <div
-    className="boxed-group boxed-group-inner"
+    className="boxed-group"
   >
     <div
-      className="spacer-bottom"
+      className="boxed-group-inner"
     >
-      login
-      : 
-      <strong
-        id="login"
+      <h2
+        className="spacer-bottom"
       >
-        luke
-      </strong>
-    </div>
-    <div
-      className="spacer-bottom"
-      id="identity-provider"
-    >
+        my_profile.login
+      </h2>
       <UserExternalIdentity
         user={
           Object {
-            "groups": Array [],
+            "email": undefined,
+            "externalProvider": "github",
+            "groups": Array [
+              "G1",
+              "G2",
+            ],
             "isLoggedIn": true,
-            "login": "luke",
+            "local": false,
+            "login": undefined,
             "name": "Skywalker",
             "scmAccounts": Array [],
           }
         }
       />
     </div>
-    <hr
-      className="account-separator"
-    />
-    <UserGroups
-      groups={Array []}
-    />
-    <hr />
-    <UserScmAccounts
-      scmAccounts={Array []}
-      user={
-        Object {
-          "groups": Array [],
-          "isLoggedIn": true,
-          "login": "luke",
-          "name": "Skywalker",
-          "scmAccounts": Array [],
-        }
-      }
-    />
+    <div
+      className="boxed-group-inner"
+    >
+      <h2
+        className="spacer-bottom"
+      >
+        my_profile.groups
+      </h2>
+      <ul
+        id="groups"
+      >
+        <li
+          className="little-spacer-bottom"
+          key="G1"
+          title="G1"
+        >
+          G1
+        </li>
+        <li
+          className="little-spacer-bottom"
+          key="G2"
+          title="G2"
+        >
+          G2
+        </li>
+      </ul>
+    </div>
+  </div>
+</div>
+`;
+
+exports[`should render correctly a local user 1`] = `
+<div
+  className="account-body account-container account-profile"
+>
+  <div
+    className="boxed-group"
+  >
+    <div
+      className="boxed-group-inner"
+    >
+      <h2
+        className="spacer-bottom"
+      >
+        my_profile.login
+      </h2>
+      <p
+        className="spacer-bottom"
+        id="login"
+      >
+        luke
+      </p>
+    </div>
+    <div
+      className="boxed-group-inner"
+    >
+      <h2
+        className="spacer-bottom"
+      >
+        my_profile.email
+      </h2>
+      <div
+        className="spacer-bottom"
+      >
+        <p
+          id="email"
+        >
+          john@doe.com
+        </p>
+      </div>
+    </div>
+    <div
+      className="boxed-group-inner"
+    >
+      <h2
+        className="spacer-bottom"
+      >
+        my_profile.groups
+      </h2>
+      <ul
+        id="groups"
+      >
+        <li
+          className="little-spacer-bottom"
+          key="G1"
+          title="G1"
+        >
+          G1
+        </li>
+        <li
+          className="little-spacer-bottom"
+          key="G2"
+          title="G2"
+        >
+          G2
+        </li>
+      </ul>
+    </div>
+    <div
+      className="boxed-group-inner"
+    >
+      <h2
+        className="spacer-bottom"
+      >
+        my_profile.scm_accounts
+        <HelpTooltip
+          className="little-spacer-left"
+          overlay="my_profile.scm_accounts.tooltip"
+        />
+      </h2>
+      <ul
+        id="scm-accounts"
+      >
+        <li
+          className="little-spacer-bottom text-ellipsis"
+          title="luke"
+        >
+          luke
+        </li>
+        <li
+          className="little-spacer-bottom text-ellipsis"
+          title="john@doe.com"
+        >
+          john@doe.com
+        </li>
+        <li
+          className="little-spacer-bottom"
+          key="SCM1"
+          title="SCM1"
+        >
+          SCM1
+        </li>
+        <li
+          className="little-spacer-bottom"
+          key="SCM2"
+          title="SCM2"
+        >
+          SCM2
+        </li>
+      </ul>
+    </div>
   </div>
 </div>
 `;
diff --git a/server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/UserDeleteAccount-test.tsx.snap b/server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/UserDeleteAccount-test.tsx.snap
deleted file mode 100644 (file)
index 05a4397..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render correctly 1`] = `
-<div>
-  <h2
-    className="spacer-bottom"
-  >
-    my_profile.delete_account
-  </h2>
-  <DeferredSpinner
-    loading={true}
-    timeout={100}
-  />
-</div>
-`;
-
-exports[`should render correctly 2`] = `
-<div>
-  <h2
-    className="spacer-bottom"
-  >
-    my_profile.delete_account
-  </h2>
-  <DeferredSpinner
-    loading={false}
-    timeout={100}
-  />
-  <UserDeleteAccountContent
-    className="list-styled no-padding big-spacer-top big-spacer-bottom"
-    organizationsSafeToDelete={
-      Array [
-        Object {
-          "key": "luke-leia",
-          "name": "Luke and Leia",
-        },
-        Object {
-          "key": "luke",
-          "name": "Luke Skywalker",
-        },
-      ]
-    }
-    organizationsToTransferOrDelete={Array []}
-  />
-  <Button
-    className="button-red"
-    disabled={false}
-    onClick={[Function]}
-    type="button"
-  >
-    delete
-  </Button>
-</div>
-`;
-
-exports[`should render correctly 3`] = `
-<div>
-  <h2
-    className="spacer-bottom"
-  >
-    my_profile.delete_account
-  </h2>
-  <DeferredSpinner
-    loading={false}
-    timeout={100}
-  />
-  <UserDeleteAccountContent
-    className="list-styled no-padding big-spacer-top big-spacer-bottom"
-    organizationsSafeToDelete={
-      Array [
-        Object {
-          "key": "luke-leia",
-          "name": "Luke and Leia",
-        },
-        Object {
-          "key": "luke",
-          "name": "Luke Skywalker",
-        },
-      ]
-    }
-    organizationsToTransferOrDelete={Array []}
-  />
-  <Button
-    className="button-red"
-    disabled={false}
-    onClick={[Function]}
-    type="button"
-  >
-    delete
-  </Button>
-  <Connect(withRouter(UserDeleteAccountModal))
-    label="my_profile.delete_account"
-    organizationsSafeToDelete={
-      Array [
-        Object {
-          "key": "luke-leia",
-          "name": "Luke and Leia",
-        },
-        Object {
-          "key": "luke",
-          "name": "Luke Skywalker",
-        },
-      ]
-    }
-    organizationsToTransferOrDelete={Array []}
-    toggleModal={[Function]}
-    user={
-      Object {
-        "externalIdentity": "luke",
-        "groups": Array [],
-        "isLoggedIn": true,
-        "login": "luke",
-        "name": "Skywalker",
-        "scmAccounts": Array [],
-      }
-    }
-  />
-</div>
-`;
diff --git a/server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/UserDeleteAccountContent-test.tsx.snap b/server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/UserDeleteAccountContent-test.tsx.snap
deleted file mode 100644 (file)
index 56ac708..0000000
+++ /dev/null
@@ -1,128 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render content correctly 1`] = `
-<ShowOrganizationsToTransferOrDelete
-  organizations={
-    Array [
-      Object {
-        "key": "luke-leia",
-        "name": "Luke and Leia",
-      },
-    ]
-  }
-/>
-`;
-
-exports[`should render content correctly 2`] = `
-<ShowOrganizations
-  className="my-class"
-  organizations={
-    Array [
-      Object {
-        "key": "luke",
-        "name": "Luke Skywalker",
-      },
-    ]
-  }
-/>
-`;
-
-exports[`should render correctly ShowOrganizations 1`] = `
-<ul>
-  <li
-    className="spacer-bottom"
-  >
-    my_profile.delete_account.info
-  </li>
-  <li
-    className="spacer-bottom"
-  >
-    <FormattedMessage
-      defaultMessage="my_profile.delete_account.data.info"
-      id="my_profile.delete_account.data.info"
-      values={
-        Object {
-          "help": <a
-            href="/documentation/user-guide/user-account/#delete-your-user-account"
-            rel="noopener noreferrer"
-            target="_blank"
-          >
-            learn_more
-          </a>,
-        }
-      }
-    />
-  </li>
-  <li
-    className="spacer-bottom"
-  >
-    <FormattedMessage
-      defaultMessage="my_profile.delete_account.info.orgs.members"
-      id="my_profile.delete_account.info.orgs.members"
-      values={
-        Object {
-          "organizations": <React.Fragment>
-            <span>
-              <Link
-                onlyActiveOnIndex={false}
-                style={Object {}}
-                to="/organizations/luke"
-              >
-                Luke Skywalker
-              </Link>
-            </span>
-          </React.Fragment>,
-        }
-      }
-    />
-  </li>
-</ul>
-`;
-
-exports[`should render correctly ShowOrganizationsToTransferOrDelete 1`] = `
-<Fragment>
-  <p
-    className="big-spacer-bottom"
-  >
-    <FormattedMessage
-      defaultMessage="my_profile.delete_account.info.orgs_to_transfer_or_delete"
-      id="my_profile.delete_account.info.orgs_to_transfer_or_delete"
-      values={
-        Object {
-          "organizations": <React.Fragment>
-            <span>
-              <Link
-                onlyActiveOnIndex={false}
-                style={Object {}}
-                to="/organizations/luke-leia"
-              >
-                Luke and Leia
-              </Link>
-            </span>
-          </React.Fragment>,
-        }
-      }
-    />
-  </p>
-  <Alert
-    className="big-spacer-bottom"
-    variant="warning"
-  >
-    <FormattedMessage
-      defaultMessage="my_profile.delete_account.info.orgs_to_transfer_or_delete.info"
-      id="my_profile.delete_account.info.orgs_to_transfer_or_delete.info"
-      values={
-        Object {
-          "link": <a
-            href="https://sieg.eu.ngrok.io/documentation/organizations/overview/#how-to-transfer-ownership-of-an-organization"
-            rel="noopener noreferrer"
-            target="_blank"
-          >
-            my_profile.delete_account.info.orgs_to_transfer_or_delete.info.link
-          </a>,
-        }
-      }
-    />
-  </Alert>
-</Fragment>
-`;
diff --git a/server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/UserDeleteAccountModal-test.tsx.snap b/server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/UserDeleteAccountModal-test.tsx.snap
deleted file mode 100644 (file)
index 5ba1d10..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`should render modal correctly 1`] = `
-<ValidationModal
-  confirmButtonText="delete"
-  header="my_profile.delete_account.modal.header.label.luke"
-  initialValues={
-    Object {
-      "login": "",
-    }
-  }
-  isDestructive={true}
-  onClose={[MockFunction]}
-  onSubmit={[Function]}
-  validate={[Function]}
->
-  <Component />
-</ValidationModal>
-`;
diff --git a/server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/UserExternalIdentity-test.tsx.snap b/server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/UserExternalIdentity-test.tsx.snap
new file mode 100644 (file)
index 0000000..9ded81f
--- /dev/null
@@ -0,0 +1,29 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render a fallback when idp is not listed 1`] = `
+<div>
+  ggithub
+  : 
+</div>
+`;
+
+exports[`should render correctly 1`] = `
+<div
+  className="identity-provider"
+  style={
+    Object {
+      "backgroundColor": "#444444",
+      "color": "#fff",
+    }
+  }
+>
+  <img
+    alt="GitHub"
+    className="little-spacer-right"
+    height="14"
+    src="/images/github.svg"
+    width="14"
+  />
+   
+</div>
+`;
index 28684bdc7582643e4a42367f34543d509604e091..f8bb2c4d80788686e5de949997ba3c018382ecd4 100644 (file)
@@ -1553,21 +1553,7 @@ groups.remove.confirmation=Are you sure you want to remove group "{user}"?
 # MY PROFILE & MY ACCOUNT
 #
 #------------------------------------------------------------------------------
-my_profile.delete_account=Delete your user account
-my_profile.delete_account.success=Account successfully deleted
-my_profile.delete_account.feedback.reason.explanation=We are sorry to see you leave.
-my_profile.delete_account.feedback.call_to_action={link} to help improve our product or offer.
-my_profile.delete_account.info=We will immediately delete your account.
-my_profile.delete_account.data.info=All your data will be removed except your login. {help}
-my_profile.delete_account.info.orgs.members=You will be removed from the members of: {organizations}.
-my_profile.delete_account.info.orgs.administrators=You will be removed from the administrators of: {organizations}.
-my_profile.delete_account.info.orgs_to_transfer_or_delete=Your account is the only administrator for the following organization(s): {organizations}.
-my_profile.delete_account.info.orgs_to_transfer_or_delete.info=You must transfer administration permissions or delete these organizations before you can delete your SonarCloud account. {link}.
-my_profile.delete_account.info.orgs_to_transfer_or_delete.info.link=See Organization Admin Guide
-my_profile.delete_account.modal.header={0}: {1}
-my_profile.delete_account.login.required=Login is required
-my_profile.delete_account.login.wrong_value=Please type your login to confirm
-my_profile.delete_account.verify=To verify, please type your user account name below
+my_profile.login=Login
 my_profile.email=Email
 my_profile.groups=Groups
 my_profile.scm_accounts=SCM Accounts
@@ -1584,7 +1570,6 @@ my_profile.sonarcloud_feature_notifications.title=SonarCloud new feature notific
 my_profile.sonarcloud_feature_notifications.description=Display a notification in the header when new features are deployed
 my_profile.per_project_notifications.title=Notifications per project
 my_profile.per_project_notifications.add=Add a project
-my_profile.warning_message=This is a definitive action. No account recovery will be possible.
 
 my_account.page=My Account
 my_account.notifications=Notifications