From: Stas Vilchik Date: Thu, 1 Feb 2018 14:55:54 +0000 (+0100) Subject: SONAR-10338 Implement /sessions/email_already_exists page X-Git-Tag: 7.5~1717 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=8ef5511f69e3f82375a0bd44c771afe4643bbca2;p=sonarqube.git SONAR-10338 Implement /sessions/email_already_exists page --- diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/EmailAlreadyExistsPage.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/EmailAlreadyExistsPage.java new file mode 100644 index 00000000000..2dfdb005d00 --- /dev/null +++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/EmailAlreadyExistsPage.java @@ -0,0 +1,48 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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. + */ +package org.sonarqube.qa.util.pageobjects; + +import static com.codeborne.selenide.Condition.text; +import static com.codeborne.selenide.Condition.visible; +import static com.codeborne.selenide.Selenide.$; + +public class EmailAlreadyExistsPage extends Navigation { + + public EmailAlreadyExistsPage shouldHaveExistingAccount(String login) { + $(".js-existing-account").shouldHave(text(login)); + return this; + } + + public EmailAlreadyExistsPage shouldHaveNewAccount(String login) { + $(".js-new-account").shouldHave(text(login)); + return this; + } + + public void clickContinue() { + $(".js-continue").click(); + $(".js-continue").shouldNotBe(visible); + } + + public void clickCancel() { + $(".js-cancel").click(); + $(".js-cancel").shouldNotBe(visible); + } + +} diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/Navigation.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/Navigation.java index f4e37dce67f..ede43929a06 100644 --- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/Navigation.java +++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/Navigation.java @@ -258,6 +258,10 @@ public class Navigation { return Selenide.$("#error"); } + public EmailAlreadyExistsPage asEmailAlreadyExistsPage() { + return new EmailAlreadyExistsPage(); + } + private static SelenideElement logInLink() { return Selenide.$(By.linkText("Log in")); } @@ -268,6 +272,7 @@ public class Navigation { /** * Safe encoding for URL parameters + * * @param parameter the parameter to escape value * @return the escaped value of parameter */ diff --git a/server/sonar-web/scripts/start.js b/server/sonar-web/scripts/start.js index 21776e5036b..53969869f33 100644 --- a/server/sonar-web/scripts/start.js +++ b/server/sonar-web/scripts/start.js @@ -35,7 +35,7 @@ const config = getConfig({ production: false }); const port = process.env.PORT || 3000; const protocol = process.env.HTTPS === 'true' ? 'https' : 'http'; const host = process.env.HOST || 'localhost'; -const proxy = 'http://localhost:9000'; +const proxy = process.env.PROXY || 'http://localhost:9000'; const compiler = setupCompiler(host, port, protocol); diff --git a/server/sonar-web/src/main/js/apps/sessions/components/EmailAlreadyExists.tsx b/server/sonar-web/src/main/js/apps/sessions/components/EmailAlreadyExists.tsx new file mode 100644 index 00000000000..2e2c92a8a61 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/sessions/components/EmailAlreadyExists.tsx @@ -0,0 +1,138 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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 { getIdentityProviders, IdentityProvider } from '../../../api/users'; +import { translate } from '../../../helpers/l10n'; +import { getBaseUrl } from '../../../helpers/urls'; + +interface Props { + location: { + query: { + email: string; + login: string; + provider: string; + existingLogin: string; + existingProvider: string; + }; + }; +} + +interface State { + identityProviders: IdentityProvider[]; + loading: boolean; +} + +export default class EmailAlreadyExists extends React.PureComponent { + mounted: boolean; + state: State = { identityProviders: [], loading: true }; + + componentDidMount() { + this.mounted = true; + this.fetchIdentityProviders(); + } + + componentWillUnmount() { + this.mounted = false; + } + + fetchIdentityProviders = () => { + this.setState({ loading: true }); + getIdentityProviders().then( + ({ identityProviders }) => { + if (this.mounted) { + this.setState({ identityProviders, loading: false }); + } + }, + () => { + if (this.mounted) { + this.setState({ loading: false }); + } + } + ); + }; + + renderIdentityProvier = (provider: string, login: string) => { + const identityProvider = this.state.identityProviders.find(p => p.key === provider); + + return identityProvider ? ( +
+ {identityProvider.name} + {login} +
+ ) : ( +
+ {provider !== 'sonarqube' && provider} {login} +
+ ); + }; + + render() { + const { query } = this.props.location; + + return ( +
+
+

+ {query.email} }} + /> +

+ {this.renderIdentityProvier(query.existingProvider, query.existingLogin)} +
+ +
+

{translate('sessions.email_already_exists.2')}

+ {this.renderIdentityProvier(query.provider, query.login)} +
+ +
+ {translate('sessions.email_already_exists.3')} +
    +
  • {translate('sessions.email_already_exists.4')}
  • +
  • {translate('sessions.email_already_exists.5')}
  • +
  • {translate('sessions.email_already_exists.6')}
  • +
+
+ +
+ + {translate('continue')} + + + {translate('cancel')} + +
+
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/sessions/components/SimpleSessionsContainer.tsx b/server/sonar-web/src/main/js/apps/sessions/components/SimpleSessionsContainer.tsx index ffca1290264..6e7df5248e9 100644 --- a/server/sonar-web/src/main/js/apps/sessions/components/SimpleSessionsContainer.tsx +++ b/server/sonar-web/src/main/js/apps/sessions/components/SimpleSessionsContainer.tsx @@ -21,7 +21,7 @@ import * as React from 'react'; import SimpleContainer from '../../../app/components/SimpleContainer'; interface Props { - children?: React.ReactElement; + children?: React.ReactNode; } export default function SimpleSessionsContainer({ children }: Props) { diff --git a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/EmailAlreadyExists-test.tsx b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/EmailAlreadyExists-test.tsx new file mode 100644 index 00000000000..f0a4baca93d --- /dev/null +++ b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/EmailAlreadyExists-test.tsx @@ -0,0 +1,58 @@ +/* + * SonarQube + * Copyright (C) 2009-2018 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 { shallow } from 'enzyme'; +import EmailAlreadyExists from '../EmailAlreadyExists'; + +jest.mock('../../../../api/users', () => ({ + getIdentityProviders: () => + Promise.resolve({ + identityProviders: [ + { + key: 'bitbucket', + name: 'Bitbucket', + iconPath: '/static/authbitbucket/bitbucket.svg', + backgroundColor: '#205081' + }, + { + key: 'github', + name: 'GitHub', + iconPath: '/static/authgithub/github.svg', + backgroundColor: '#444444' + } + ] + }) +})); + +it('render', async () => { + const query = { + email: 'mail@example.com', + login: 'foo', + provider: 'github', + existingLogin: 'bar', + existingProvider: 'bitbucket' + }; + const wrapper = shallow(); + (wrapper.instance() as EmailAlreadyExists).mounted = true; + (wrapper.instance() as EmailAlreadyExists).fetchIdentityProviders(); + await new Promise(setImmediate); + wrapper.update(); + expect(wrapper).toMatchSnapshot(); +}); diff --git a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/EmailAlreadyExists-test.tsx.snap b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/EmailAlreadyExists-test.tsx.snap new file mode 100644 index 00000000000..95ec95ce038 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/EmailAlreadyExists-test.tsx.snap @@ -0,0 +1,108 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`render 1`] = ` +
+
+

+ + mail@example.com + , + } + } + /> +

+
+ Bitbucket + bar +
+
+
+

+ sessions.email_already_exists.2 +

+
+ GitHub + foo +
+
+
+ sessions.email_already_exists.3 +
    +
  • + sessions.email_already_exists.4 +
  • +
  • + sessions.email_already_exists.5 +
  • +
  • + sessions.email_already_exists.6 +
  • +
+
+ +
+`; diff --git a/server/sonar-web/src/main/js/apps/sessions/routes.ts b/server/sonar-web/src/main/js/apps/sessions/routes.ts index d5f4afec31e..92b4d569909 100644 --- a/server/sonar-web/src/main/js/apps/sessions/routes.ts +++ b/server/sonar-web/src/main/js/apps/sessions/routes.ts @@ -37,6 +37,12 @@ const routes = [ getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { import('./components/Unauthorized').then(i => callback(null, i.default)); } + }, + { + path: 'email_already_exists', + getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) { + import('./components/EmailAlreadyExists').then(i => callback(null, i.default)); + } } ]; 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 f4e5b41e0eb..e9f5f8a0ed2 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -527,6 +527,12 @@ process.fail=Failed #------------------------------------------------------------------------------ sessions.log_in=Log in +sessions.email_already_exists.1=The email address {email} is already associated to this user account: +sessions.email_already_exists.2=By clicking on "Continue" you will associate this email address to a new user account: +sessions.email_already_exists.3=This means the following: +sessions.email_already_exists.4=Your email address will be erased from this account. +sessions.email_already_exists.5=You will no longer receive email notifications from this account. +sessions.email_already_exists.6=Issues won't be automatically assigned on the first account anymore. #------------------------------------------------------------------------------