diff options
author | Philippe Perrin <philippe.perrin@sonarsource.com> | 2019-10-11 09:32:45 +0200 |
---|---|---|
committer | SonarTech <sonartech@sonarsource.com> | 2019-10-16 11:09:14 +0200 |
commit | 86cc520a254ece5c3e524e51c6505ff5674b55e2 (patch) | |
tree | 5e6f670ff7401518d6c3898178ec35b2ed6a177d | |
parent | bb707122a8dba99ce7c8f2e122b121fdce90ff6c (diff) | |
download | sonarqube-86cc520a254ece5c3e524e51c6505ff5674b55e2.tar.gz sonarqube-86cc520a254ece5c3e524e51c6505ff5674b55e2.zip |
Improve user profile & unit tests
23 files changed, 385 insertions, 1282 deletions
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 index d91059bf630..00000000000 --- a/server/sonar-web/src/main/js/app/components/AccountDeleted.tsx +++ /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 index 608b22dcf2f..00000000000 --- a/server/sonar-web/src/main/js/app/components/__tests__/AccountDeleted-test.tsx +++ /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 index 8bffcc4ae6e..00000000000 --- a/server/sonar-web/src/main/js/app/components/__tests__/__snapshots__/AccountDeleted-test.tsx.snap +++ /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> -`; diff --git a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx index 1eb7843fbcc..0e91d2c37fb 100644 --- a/server/sonar-web/src/main/js/app/utils/startReactApp.tsx +++ b/server/sonar-web/src/main/js/app/utils/startReactApp.tsx @@ -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} /> diff --git a/server/sonar-web/src/main/js/apps/account/account.css b/server/sonar-web/src/main/js/apps/account/account.css index eab942f9e5e..f7575bfc714 100644 --- a/server/sonar-web/src/main/js/apps/account/account.css +++ b/server/sonar-web/src/main/js/apps/account/account.css @@ -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); } diff --git a/server/sonar-web/src/main/js/apps/account/profile/Profile.tsx b/server/sonar-web/src/main/js/apps/account/profile/Profile.tsx index 4b55078de41..a05a7d04302 100644 --- a/server/sonar-web/src/main/js/apps/account/profile/Profile.tsx +++ b/server/sonar-web/src/main/js/apps/account/profile/Profile.tsx @@ -18,59 +18,123 @@ * 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 index d114dc9d2fb..00000000000 --- a/server/sonar-web/src/main/js/apps/account/profile/UserDeleteAccount.tsx +++ /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 index aaad598307b..00000000000 --- a/server/sonar-web/src/main/js/apps/account/profile/UserDeleteAccountContent.tsx +++ /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 index 80ea464225e..00000000000 --- a/server/sonar-web/src/main/js/apps/account/profile/UserDeleteAccountModal.tsx +++ /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)); diff --git a/server/sonar-web/src/main/js/apps/account/profile/UserExternalIdentity.tsx b/server/sonar-web/src/main/js/apps/account/profile/UserExternalIdentity.tsx index 523529ae9ca..a4add5f55e1 100644 --- a/server/sonar-web/src/main/js/apps/account/profile/UserExternalIdentity.tsx +++ b/server/sonar-web/src/main/js/apps/account/profile/UserExternalIdentity.tsx @@ -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 index a818818ddd0..00000000000 --- a/server/sonar-web/src/main/js/apps/account/profile/UserGroups.tsx +++ /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 index 9055fc5de7f..00000000000 --- a/server/sonar-web/src/main/js/apps/account/profile/UserScmAccounts.tsx +++ /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> - ); -} diff --git a/server/sonar-web/src/main/js/apps/account/profile/__tests__/Profile-test.tsx b/server/sonar-web/src/main/js/apps/account/profile/__tests__/Profile-test.tsx index 479effa96fb..175fada451d 100644 --- a/server/sonar-web/src/main/js/apps/account/profile/__tests__/Profile-test.tsx +++ b/server/sonar-web/src/main/js/apps/account/profile/__tests__/Profile-test.tsx @@ -19,41 +19,36 @@ */ 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 index 215e94f194d..00000000000 --- a/server/sonar-web/src/main/js/apps/account/profile/__tests__/UserDeleteAccount-test.tsx +++ /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 index 37aa8ec4321..00000000000 --- a/server/sonar-web/src/main/js/apps/account/profile/__tests__/UserDeleteAccountContent-test.tsx +++ /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 index 0ad5db24db2..00000000000 --- a/server/sonar-web/src/main/js/apps/account/profile/__tests__/UserDeleteAccountModal-test.tsx +++ /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 index 00000000000..bbb6e43168b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/account/profile/__tests__/UserExternalIdentity-test.tsx @@ -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 + }) + }} + /> + ); +} diff --git a/server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/Profile-test.tsx.snap b/server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/Profile-test.tsx.snap index e896064c035..3612928afed 100644 --- a/server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/Profile-test.tsx.snap +++ b/server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/Profile-test.tsx.snap @@ -1,58 +1,179 @@ // 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 index 05a4397a565..00000000000 --- a/server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/UserDeleteAccount-test.tsx.snap +++ /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 index 56ac708fb53..00000000000 --- a/server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/UserDeleteAccountContent-test.tsx.snap +++ /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 index 5ba1d10e4ad..00000000000 --- a/server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/UserDeleteAccountModal-test.tsx.snap +++ /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 index 00000000000..9ded81f8129 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/account/profile/__tests__/__snapshots__/UserExternalIdentity-test.tsx.snap @@ -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> +`; diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index 28684bdc758..f8bb2c4d807 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -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 |