]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-10646 SONAR-10325 Update SonarCloud login page style
authorGrégoire Aubert <gregoire.aubert@sonarsource.com>
Fri, 27 Apr 2018 07:19:46 +0000 (09:19 +0200)
committerSonarTech <sonartech@sonarsource.com>
Thu, 3 May 2018 18:20:49 +0000 (20:20 +0200)
22 files changed:
server/sonar-web/public/images/sc-icon.svg [new file with mode: 0644]
server/sonar-web/src/main/js/apps/sessions/components/Login.css [new file with mode: 0644]
server/sonar-web/src/main/js/apps/sessions/components/Login.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/sessions/components/LoginContainer.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/sessions/components/LoginForm.css
server/sonar-web/src/main/js/apps/sessions/components/LoginForm.tsx
server/sonar-web/src/main/js/apps/sessions/components/LoginFormContainer.tsx [deleted file]
server/sonar-web/src/main/js/apps/sessions/components/LoginSonarCloud.css [new file with mode: 0644]
server/sonar-web/src/main/js/apps/sessions/components/LoginSonarCloud.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/sessions/components/OAuthProviders.tsx
server/sonar-web/src/main/js/apps/sessions/components/__tests__/EmailAlreadyExists-test.tsx
server/sonar-web/src/main/js/apps/sessions/components/__tests__/Login-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/sessions/components/__tests__/LoginForm-test.tsx
server/sonar-web/src/main/js/apps/sessions/components/__tests__/LoginSonarCloud-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/sessions/components/__tests__/OAuthProviders-test.tsx [new file with mode: 0644]
server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/EmailAlreadyExists-test.tsx.snap
server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/Login-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/LoginForm-test.tsx.snap
server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/LoginSonarCloud-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/OAuthProviders-test.tsx.snap [new file with mode: 0644]
server/sonar-web/src/main/js/apps/sessions/routes.ts
sonar-core/src/main/resources/org/sonar/l10n/core.properties

diff --git a/server/sonar-web/public/images/sc-icon.svg b/server/sonar-web/public/images/sc-icon.svg
new file mode 100644 (file)
index 0000000..bc3d84e
--- /dev/null
@@ -0,0 +1 @@
+<svg viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd" stroke-linejoin="round" stroke-miterlimit="1.414"><path d="M12.625 6.154a3.991 3.991 0 0 0-1.902-1.279v-.046C10.723 2.65 8.93.857 6.75.857c-2.179 0-3.972 1.793-3.972 3.972v.053A3.982 3.982 0 0 0 0 8.671c0 2.179 1.793 3.972 3.972 3.972a3.978 3.978 0 0 0 2.791-1.144 3.97 3.97 0 0 0 2.766 1.122c2.178 0 3.971-1.793 3.971-3.972 0-.905-.31-1.784-.877-2.489l.002-.006zm-3.073 5.489a2.99 2.99 0 0 1-2.973-2.971c0-.275-.225-.5-.5-.5a.501.501 0 0 0-.499.5 3.952 3.952 0 0 0 .56 2.032 2.971 2.971 0 0 1-2.164.936 2.985 2.985 0 0 1-2.97-2.97 2.985 2.985 0 0 1 2.97-2.971c.35 0 .697.062 1.026.183h.012c.114.038.223.09.324.155a.5.5 0 1 0 .65-.759 2.224 2.224 0 0 0-.646-.341 3.974 3.974 0 0 0-1.369-.243h-.192A2.985 2.985 0 0 1 6.75 1.85a2.986 2.986 0 0 1 2.972 2.972c0 .96-.466 1.863-1.249 2.42a.5.5 0 1 0 .58.814 3.983 3.983 0 0 0 1.526-2.184 2.979 2.979 0 0 1 1.941 2.789 2.986 2.986 0 0 1-2.969 2.972l.001.01z" fill="#f3702a" fill-rule="nonzero"/></svg>
\ No newline at end of file
diff --git a/server/sonar-web/src/main/js/apps/sessions/components/Login.css b/server/sonar-web/src/main/js/apps/sessions/components/Login.css
new file mode 100644 (file)
index 0000000..e5d49d4
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ */
+.login-page {
+  padding-top: 10vh;
+}
+
+.login-title {
+  margin-bottom: 40px;
+  line-height: 1.5;
+  font-size: 24px;
+  font-weight: 300;
+  text-align: center;
+}
diff --git a/server/sonar-web/src/main/js/apps/sessions/components/Login.tsx b/server/sonar-web/src/main/js/apps/sessions/components/Login.tsx
new file mode 100644 (file)
index 0000000..7af866c
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * 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 LoginForm from './LoginForm';
+import OAuthProviders from './OAuthProviders';
+import { IdentityProvider } from '../../../app/types';
+import { translate } from '../../../helpers/l10n';
+import './Login.css';
+
+interface Props {
+  identityProviders: IdentityProvider[];
+  onSubmit: (login: string, password: string) => Promise<void>;
+  returnTo: string;
+}
+
+export default function Login({ identityProviders, onSubmit, returnTo }: Props) {
+  return (
+    <div className="login-page" id="login_form">
+      <h1 className="login-title text-center">{translate('login.login_to_sonarqube')}</h1>
+
+      {identityProviders.length > 0 && (
+        <OAuthProviders identityProviders={identityProviders} returnTo={returnTo} />
+      )}
+
+      <LoginForm collapsed={identityProviders.length > 0} onSubmit={onSubmit} returnTo={returnTo} />
+    </div>
+  );
+}
diff --git a/server/sonar-web/src/main/js/apps/sessions/components/LoginContainer.tsx b/server/sonar-web/src/main/js/apps/sessions/components/LoginContainer.tsx
new file mode 100644 (file)
index 0000000..04396a3
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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 * as PropTypes from 'prop-types';
+import { connect } from 'react-redux';
+import Login from './Login';
+import LoginSonarCloud from './LoginSonarCloud';
+import { doLogin } from '../../../store/rootActions';
+import { getIdentityProviders } from '../../../api/users';
+import { IdentityProvider } from '../../../app/types';
+import { getBaseUrl } from '../../../helpers/urls';
+
+interface Props {
+  doLogin: (login: string, password: string) => Promise<void>;
+  location: {
+    hash?: string;
+    pathName: string;
+    query: {
+      return_to?: string; // eslint-disable-line camelcase
+    };
+  };
+}
+
+interface State {
+  identityProviders?: IdentityProvider[];
+}
+
+class LoginContainer extends React.PureComponent<Props, State> {
+  mounted = false;
+
+  static contextTypes = {
+    onSonarCloud: PropTypes.bool
+  };
+
+  state: State = {};
+
+  componentDidMount() {
+    this.mounted = true;
+    getIdentityProviders().then(
+      identityProvidersResponse => {
+        if (this.mounted) {
+          this.setState({
+            identityProviders: identityProvidersResponse.identityProviders
+          });
+        }
+      },
+      () => {}
+    );
+  }
+
+  componentWillUnmount() {
+    this.mounted = false;
+  }
+
+  getReturnUrl = () => {
+    const { location } = this.props;
+    const queryReturnTo = location.query['return_to'];
+    return queryReturnTo ? `${queryReturnTo}${location.hash}` : `${getBaseUrl()}/`;
+  };
+
+  handleSuccessfulLogin = () => {
+    window.location.href = this.getReturnUrl();
+  };
+
+  handleSubmit = (login: string, password: string) => {
+    return this.props.doLogin(login, password).then(this.handleSuccessfulLogin, () => {});
+  };
+
+  render() {
+    const { identityProviders } = this.state;
+    if (!identityProviders) {
+      return null;
+    }
+
+    if (this.context.onSonarCloud) {
+      return (
+        <LoginSonarCloud
+          identityProviders={identityProviders}
+          onSubmit={this.handleSubmit}
+          returnTo={this.getReturnUrl()}
+        />
+      );
+    }
+
+    return (
+      <Login
+        identityProviders={identityProviders}
+        onSubmit={this.handleSubmit}
+        returnTo={this.getReturnUrl()}
+      />
+    );
+  }
+}
+
+const mapStateToProps = null;
+const mapDispatchToProps = { doLogin };
+
+export default connect(mapStateToProps, mapDispatchToProps)(LoginContainer as any);
index fc74e4bba23c4ffec0f5ceb48ebafa67eee16efe..7eafbdb4e75c80ffae1c5404dd40ee93e869fef3 100644 (file)
@@ -17,9 +17,6 @@
  * along with this program; if not, write to the Free Software Foundation,
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
-.login-page {
-  padding-top: 10vh;
-}
 
 .login-form {
   width: 300px;
   margin-right: auto;
 }
 
-.login-title {
-  margin-bottom: 40px;
-  line-height: 1.5;
-  font-size: 24px;
-  font-weight: 300;
-  text-align: center;
-}
-
 .login-input {
   width: 100% !important;
   height: auto !important;
index 6cdc0087e028e21aa309792d8041173e81ac3484..ab0cecf826ed764eb4cc36578ada7ba80778b2cf 100644 (file)
  */
 import * as React from 'react';
 import { Link } from 'react-router';
-import * as classNames from 'classnames';
-import OAuthProviders from './OAuthProviders';
+import DeferredSpinner from '../../../components/common/DeferredSpinner';
 import GlobalMessagesContainer from '../../../app/components/GlobalMessagesContainer';
-import { IdentityProvider } from '../../../app/types';
 import { SubmitButton } from '../../../components/ui/buttons';
 import { translate } from '../../../helpers/l10n';
-import DeferredSpinner from '../../../components/common/DeferredSpinner';
 import './LoginForm.css';
 
 interface Props {
-  onSonarCloud: boolean;
-  identityProviders: IdentityProvider[];
+  collapsed?: boolean;
   onSubmit: (login: string, password: string) => Promise<void>;
   returnTo: string;
 }
@@ -46,7 +42,7 @@ export default class LoginForm extends React.PureComponent<Props, State> {
   constructor(props: Props) {
     super(props);
     this.state = {
-      collapsed: props.identityProviders.length > 0,
+      collapsed: Boolean(props.collapsed),
       loading: false,
       login: '',
       password: ''
@@ -57,7 +53,7 @@ export default class LoginForm extends React.PureComponent<Props, State> {
     this.setState({ loading: false });
   };
 
-  handleSubmit = (event: React.SyntheticEvent<HTMLFormElement>) => {
+  handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
     event.preventDefault();
     this.setState({ loading: true });
     this.props
@@ -65,94 +61,80 @@ export default class LoginForm extends React.PureComponent<Props, State> {
       .then(this.stopLoading, this.stopLoading);
   };
 
-  handleMoreOptionsClick = (event: React.SyntheticEvent<HTMLAnchorElement>) => {
+  handleMoreOptionsClick = (event: React.MouseEvent<HTMLAnchorElement>) => {
     event.preventDefault();
     this.setState({ collapsed: false });
   };
 
-  handleLoginChange = (event: React.SyntheticEvent<HTMLInputElement>) =>
+  handleLoginChange = (event: React.ChangeEvent<HTMLInputElement>) =>
     this.setState({ login: event.currentTarget.value });
 
-  handlePwdChange = (event: React.SyntheticEvent<HTMLInputElement>) =>
+  handlePwdChange = (event: React.ChangeEvent<HTMLInputElement>) =>
     this.setState({ password: event.currentTarget.value });
 
   render() {
-    const loginTitle = this.props.onSonarCloud
-      ? translate('login.login_to_sonarcloud')
-      : translate('login.login_to_sonarqube');
-
+    if (this.state.collapsed) {
+      return (
+        <div className="text-center">
+          <a
+            className="small text-muted js-more-options"
+            href="#"
+            onClick={this.handleMoreOptionsClick}>
+            {translate('login.more_options')}
+          </a>
+        </div>
+      );
+    }
     return (
-      <div className="login-page" id="login_form">
-        <h1 className="login-title text-center">{loginTitle}</h1>
+      <form className="login-form" onSubmit={this.handleSubmit}>
+        <GlobalMessagesContainer />
 
-        {this.props.identityProviders.length > 0 && (
-          <OAuthProviders
-            identityProviders={this.props.identityProviders}
-            returnTo={this.props.returnTo}
+        <div className="big-spacer-bottom">
+          <label className="login-label" htmlFor="login">
+            {translate('login')}
+          </label>
+          <input
+            autoFocus={true}
+            className="login-input"
+            id="login"
+            maxLength={255}
+            name="login"
+            onChange={this.handleLoginChange}
+            placeholder={translate('login')}
+            required={true}
+            type="text"
+            value={this.state.login}
           />
-        )}
-
-        {this.state.collapsed ? (
-          <div className="text-center">
-            <a
-              className="small text-muted js-more-options"
-              href="#"
-              onClick={this.handleMoreOptionsClick}>
-              {translate('login.more_options')}
-            </a>
-          </div>
-        ) : (
-          <form className="login-form" onSubmit={this.handleSubmit}>
-            <GlobalMessagesContainer />
-
-            <div className="big-spacer-bottom">
-              <label className="login-label" htmlFor="login">
-                {translate('login')}
-              </label>
-              <input
-                autoFocus={true}
-                className="login-input"
-                id="login"
-                maxLength={255}
-                name="login"
-                onChange={this.handleLoginChange}
-                placeholder={translate('login')}
-                required={true}
-                type="text"
-                value={this.state.login}
-              />
-            </div>
+        </div>
 
-            <div className="big-spacer-bottom">
-              <label className="login-label" htmlFor="password">
-                {translate('password')}
-              </label>
-              <input
-                className="login-input"
-                id="password"
-                name="password"
-                onChange={this.handlePwdChange}
-                placeholder={translate('password')}
-                required={true}
-                type="password"
-                value={this.state.password}
-              />
-            </div>
+        <div className="big-spacer-bottom">
+          <label className="login-label" htmlFor="password">
+            {translate('password')}
+          </label>
+          <input
+            className="login-input"
+            id="password"
+            name="password"
+            onChange={this.handlePwdChange}
+            placeholder={translate('password')}
+            required={true}
+            type="password"
+            value={this.state.password}
+          />
+        </div>
 
-            <div>
-              <div className="text-right overflow-hidden">
-                <DeferredSpinner className="spacer-right" loading={this.state.loading} />
-                <SubmitButton className={classNames({ disabled: this.state.loading })}>
-                  {translate('sessions.log_in')}
-                </SubmitButton>
-                <Link className="spacer-left" to="/">
-                  {translate('cancel')}
-                </Link>
-              </div>
-            </div>
-          </form>
-        )}
-      </div>
+        <div>
+          <div className="text-right overflow-hidden">
+            <DeferredSpinner className="spacer-right" loading={this.state.loading} />
+            <SubmitButton disabled={this.state.loading}>
+              {translate('sessions.log_in')}
+            </SubmitButton>
+            <Link className="spacer-left" to="/">
+              {translate('cancel')}
+            </Link>
+          </div>
+        </div>
+      </form>
     );
   }
 }
diff --git a/server/sonar-web/src/main/js/apps/sessions/components/LoginFormContainer.tsx b/server/sonar-web/src/main/js/apps/sessions/components/LoginFormContainer.tsx
deleted file mode 100644 (file)
index 63c3f7b..0000000
+++ /dev/null
@@ -1,99 +0,0 @@
-/*
- * 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 * as PropTypes from 'prop-types';
-import { connect } from 'react-redux';
-import LoginForm from './LoginForm';
-import { doLogin } from '../../../store/rootActions';
-import { getIdentityProviders } from '../../../api/users';
-import { IdentityProvider } from '../../../app/types';
-import { getBaseUrl } from '../../../helpers/urls';
-
-interface Props {
-  doLogin: (login: string, password: string) => Promise<void>;
-  location: { hash?: string; pathName: string; query: { return_to?: string } };
-}
-
-interface State {
-  identityProviders?: IdentityProvider[];
-}
-
-class LoginFormContainer extends React.PureComponent<Props, State> {
-  mounted = false;
-
-  static contextTypes = {
-    onSonarCloud: PropTypes.bool
-  };
-
-  state: State = {};
-
-  componentDidMount() {
-    this.mounted = true;
-    getIdentityProviders().then(
-      identityProvidersResponse => {
-        if (this.mounted) {
-          this.setState({
-            identityProviders: identityProvidersResponse.identityProviders
-          });
-        }
-      },
-      () => {}
-    );
-  }
-
-  componentWillUnmount() {
-    this.mounted = false;
-  }
-
-  getReturnUrl = () => {
-    const { location } = this.props;
-    const queryReturnTo = location.query['return_to'];
-    return queryReturnTo ? `${queryReturnTo}${location.hash}` : `${getBaseUrl()}/`;
-  };
-
-  handleSuccessfulLogin = () => {
-    window.location.href = this.getReturnUrl();
-  };
-
-  handleSubmit = (login: string, password: string) => {
-    return this.props.doLogin(login, password).then(this.handleSuccessfulLogin, () => {});
-  };
-
-  render() {
-    const { identityProviders } = this.state;
-    if (!identityProviders) {
-      return null;
-    }
-
-    return (
-      <LoginForm
-        identityProviders={identityProviders}
-        onSonarCloud={this.context.onSonarCloud}
-        onSubmit={this.handleSubmit}
-        returnTo={this.getReturnUrl()}
-      />
-    );
-  }
-}
-
-const mapStateToProps = null;
-const mapDispatchToProps = { doLogin };
-
-export default connect(mapStateToProps, mapDispatchToProps)(LoginFormContainer as any);
diff --git a/server/sonar-web/src/main/js/apps/sessions/components/LoginSonarCloud.css b/server/sonar-web/src/main/js/apps/sessions/components/LoginSonarCloud.css
new file mode 100644 (file)
index 0000000..b618673
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * 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.
+ */
+.sonarcloud-login-page {
+  margin-top: 15vh;
+  width: 200px;
+  margin-left: auto;
+  margin-right: auto;
+  padding: calc(2 * var(--gridSize)) 20px;
+}
+
+.sonarcloud-login-title {
+  line-height: 1.5;
+  font-size: var(--bigFontSize);
+  font-weight: 300;
+  width: 135px;
+  margin: var(--gridSize) auto calc(2 * var(--gridSize));
+}
+
+.sonarcloud-oauth-providers.oauth-providers > ul > li {
+  margin-bottom: var(--gridSize);
+}
+
+.sonarcloud-oauth-providers.oauth-providers > ul > li > a > span {
+  font-weight: 600;
+  padding-left: calc(1.5 * var(--gridSize));
+}
+
+.sonarcloud-oauth-providers.oauth-providers > ul > li > a > span::before {
+  content: '';
+  border-left: 1px var(--gray71) solid;
+  height: 10px;
+  opacity: 0.4;
+  margin-right: calc(1.5 * var(--gridSize));
+}
+
+.sonarcloud-oauth-providers.oauth-providers .oauth-providers-help {
+  right: -22px;
+}
diff --git a/server/sonar-web/src/main/js/apps/sessions/components/LoginSonarCloud.tsx b/server/sonar-web/src/main/js/apps/sessions/components/LoginSonarCloud.tsx
new file mode 100644 (file)
index 0000000..9df3ffd
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * 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 OAuthProviders from './OAuthProviders';
+import { IdentityProvider } from '../../../app/types';
+import { getHostUrl } from '../../../helpers/urls';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
+import './LoginSonarCloud.css';
+
+interface Props {
+  identityProviders: IdentityProvider[];
+  returnTo: string;
+}
+
+export default function LoginSonarCloud({ identityProviders, returnTo }: Props) {
+  return (
+    <div className="sonarcloud-login-page boxed-group boxed-group-inner" id="login_form">
+      <div className="text-center">
+        <img
+          alt="SonarCloud logo"
+          height={36}
+          src={`${getHostUrl()}/images/sc-icon.svg`}
+          width={36}
+        />
+        <h1 className="sonarcloud-login-title">
+          {translate('login.login_or_signup_to_sonarcloud')}
+        </h1>
+      </div>
+
+      <OAuthProviders
+        className="sonarcloud-oauth-providers"
+        formatLabel={formatLabel}
+        identityProviders={identityProviders}
+        returnTo={returnTo}
+      />
+    </div>
+  );
+}
+
+function formatLabel(name: string) {
+  return translateWithParameters('login.with_x', name);
+}
index 5664080f8288aa8a6806de8c9f97fb77de45fb22..817cb3d2ade8e819474964f1605abd493812daa7 100644 (file)
@@ -18,6 +18,7 @@
  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
  */
 import * as React from 'react';
+import * as classNames from 'classnames';
 import { translateWithParameters } from '../../../helpers/l10n';
 import * as theme from '../../../app/theme';
 import { IdentityProvider } from '../../../app/types';
@@ -28,13 +29,16 @@ import { getBaseUrl } from '../../../helpers/urls';
 import './OAuthProviders.css';
 
 interface Props {
+  className?: string;
+  formatLabel?: (name: string) => React.ReactNode;
   identityProviders: IdentityProvider[];
   returnTo: string;
 }
 
 export default function OAuthProviders(props: Props) {
+  const formatFunction = props.formatLabel || defaultFormatLabel;
   return (
-    <section className="oauth-providers">
+    <section className={classNames('oauth-providers', props.className)}>
       <ul>
         {props.identityProviders.map(identityProvider => (
           <li key={identityProvider.key}>
@@ -49,11 +53,11 @@ export default function OAuthProviders(props: Props) {
               }}>
               <img
                 alt={identityProvider.name}
-                width="20"
                 height="20"
                 src={getBaseUrl() + identityProvider.iconPath}
+                width="20"
               />
-              <span>{defaultFormatLabel(identityProvider.name)}</span>
+              <span>{formatFunction(identityProvider.name)}</span>
             </a>
             {identityProvider.helpMessage && (
               <Tooltip overlay={identityProvider.helpMessage}>
index 81b2176c785422d85dd23d20528311f37830ff37..1540dd3e55fe5a92b93004b790ab19fba79954a4 100644 (file)
@@ -30,7 +30,7 @@ jest.mock('../../../../api/users', () => ({
           key: 'bitbucket',
           name: 'Bitbucket',
           iconPath: '/static/authbitbucket/bitbucket.svg',
-          backgroundColor: '#205081'
+          backgroundColor: '#0052cc'
         },
         {
           key: 'github',
diff --git a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/Login-test.tsx b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/Login-test.tsx
new file mode 100644 (file)
index 0000000..82dcc6e
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * 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 Login from '../Login';
+
+const identityProvider = {
+  backgroundColor: '#000',
+  iconPath: '/some/path',
+  key: 'foo',
+  name: 'foo'
+};
+
+it('logs in with form alone', () => {
+  const wrapper = shallow(<Login identityProviders={[]} onSubmit={jest.fn()} returnTo="" />);
+  expect(wrapper).toMatchSnapshot();
+});
+
+it('logs in with identity provider', () => {
+  const wrapper = shallow(
+    <Login identityProviders={[identityProvider]} onSubmit={jest.fn()} returnTo="" />
+  );
+  expect(wrapper).toMatchSnapshot();
+});
index 8ba3c8e1d8fe66250ec65493981b30ac9f016171..e1cbea70b3aaac9e48f9b8f5b61ab9c622d14ed1 100644 (file)
@@ -22,18 +22,9 @@ import { shallow } from 'enzyme';
 import LoginForm from '../LoginForm';
 import { change, click, submit, waitAndUpdate } from '../../../../helpers/testUtils';
 
-const identityProvider = {
-  backgroundColor: '#000',
-  iconPath: '/some/path',
-  key: 'foo',
-  name: 'foo'
-};
-
 it('logs in with simple credentials', () => {
   const onSubmit = jest.fn(() => Promise.resolve());
-  const wrapper = shallow(
-    <LoginForm identityProviders={[]} onSonarCloud={false} onSubmit={onSubmit} returnTo="" />
-  );
+  const wrapper = shallow(<LoginForm onSubmit={onSubmit} returnTo="" />);
   expect(wrapper).toMatchSnapshot();
 
   change(wrapper.find('#login'), 'admin');
@@ -45,9 +36,7 @@ it('logs in with simple credentials', () => {
 
 it('should display a spinner and disabled button while loading', async () => {
   const onSubmit = jest.fn(() => Promise.resolve());
-  const wrapper = shallow(
-    <LoginForm identityProviders={[]} onSonarCloud={false} onSubmit={onSubmit} returnTo="" />
-  );
+  const wrapper = shallow(<LoginForm onSubmit={onSubmit} returnTo="" />);
 
   change(wrapper.find('#login'), 'admin');
   change(wrapper.find('#password'), 'admin');
@@ -58,27 +47,8 @@ it('should display a spinner and disabled button while loading', async () => {
   await waitAndUpdate(wrapper);
 });
 
-it('logs in with identity provider', () => {
-  const wrapper = shallow(
-    <LoginForm
-      identityProviders={[identityProvider]}
-      onSonarCloud={false}
-      onSubmit={jest.fn()}
-      returnTo=""
-    />
-  );
-  expect(wrapper).toMatchSnapshot();
-});
-
 it('expands more options', () => {
-  const wrapper = shallow(
-    <LoginForm
-      identityProviders={[identityProvider]}
-      onSonarCloud={false}
-      onSubmit={jest.fn()}
-      returnTo=""
-    />
-  );
+  const wrapper = shallow(<LoginForm collapsed={true} onSubmit={jest.fn()} returnTo="" />);
   expect(wrapper).toMatchSnapshot();
 
   click(wrapper.find('.js-more-options'));
diff --git a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/LoginSonarCloud-test.tsx b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/LoginSonarCloud-test.tsx
new file mode 100644 (file)
index 0000000..0fa5c7a
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * 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 LoginSonarCloud from '../LoginSonarCloud';
+
+const identityProvider = {
+  backgroundColor: '#000',
+  iconPath: '/some/path',
+  key: 'foo',
+  name: 'foo'
+};
+
+it('logs in with identity provider', () => {
+  const wrapper = shallow(
+    <LoginSonarCloud identityProviders={[identityProvider]} onSubmit={jest.fn()} returnTo="" />
+  );
+  expect(wrapper).toMatchSnapshot();
+});
diff --git a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/OAuthProviders-test.tsx b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/OAuthProviders-test.tsx
new file mode 100644 (file)
index 0000000..65755a2
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * 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 OAuthProviders from '../OAuthProviders';
+
+const identityProviders = [
+  {
+    backgroundColor: '#000',
+    iconPath: '/some/path',
+    key: 'foo',
+    name: 'Foo'
+  },
+  {
+    backgroundColor: '#00F',
+    helpMessage: 'Help message!',
+    iconPath: '/icon/path',
+    key: 'bar',
+    name: 'Bar'
+  }
+];
+
+it('should render correctly', () => {
+  expect(
+    shallow(<OAuthProviders identityProviders={identityProviders} returnTo="" />)
+  ).toMatchSnapshot();
+});
+
+it('should use the custom label formatter', () => {
+  expect(
+    shallow(
+      <OAuthProviders
+        formatLabel={name => 'custom_format.' + name}
+        identityProviders={[identityProviders[0]]}
+        returnTo=""
+      />
+    )
+  ).toMatchSnapshot();
+});
index 096b7135b1c670a9dee14ce576fd87e2ffbfffa9..f2da910473171e50c2122ff0afe5864b1a747441 100644 (file)
@@ -31,7 +31,7 @@ exports[`render 1`] = `
         className="identity-provider"
         style={
           Object {
-            "backgroundColor": "#205081",
+            "backgroundColor": "#0052cc",
             "color": "#fff",
           }
         }
diff --git a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/Login-test.tsx.snap b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/Login-test.tsx.snap
new file mode 100644 (file)
index 0000000..1d254a9
--- /dev/null
@@ -0,0 +1,50 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`logs in with form alone 1`] = `
+<div
+  className="login-page"
+  id="login_form"
+>
+  <h1
+    className="login-title text-center"
+  >
+    login.login_to_sonarqube
+  </h1>
+  <LoginForm
+    collapsed={false}
+    onSubmit={[MockFunction]}
+    returnTo=""
+  />
+</div>
+`;
+
+exports[`logs in with identity provider 1`] = `
+<div
+  className="login-page"
+  id="login_form"
+>
+  <h1
+    className="login-title text-center"
+  >
+    login.login_to_sonarqube
+  </h1>
+  <OAuthProviders
+    identityProviders={
+      Array [
+        Object {
+          "backgroundColor": "#000",
+          "iconPath": "/some/path",
+          "key": "foo",
+          "name": "foo",
+        },
+      ]
+    }
+    returnTo=""
+  />
+  <LoginForm
+    collapsed={true}
+    onSubmit={[MockFunction]}
+    returnTo=""
+  />
+</div>
+`;
index c2082daeaff2df334aab4cac160c6170d04636db..aa19aded41e77d52371a3d08d842a8f38aee9b9f 100644 (file)
 
 exports[`expands more options 1`] = `
 <div
-  className="login-page"
-  id="login_form"
+  className="text-center"
 >
-  <h1
-    className="login-title text-center"
+  <a
+    className="small text-muted js-more-options"
+    href="#"
+    onClick={[Function]}
   >
-    login.login_to_sonarqube
-  </h1>
-  <OAuthProviders
-    identityProviders={
-      Array [
-        Object {
-          "backgroundColor": "#000",
-          "iconPath": "/some/path",
-          "key": "foo",
-          "name": "foo",
-        },
-      ]
-    }
-    returnTo=""
-  />
-  <div
-    className="text-center"
-  >
-    <a
-      className="small text-muted js-more-options"
-      href="#"
-      onClick={[Function]}
-    >
-      login.more_options
-    </a>
-  </div>
+    login.more_options
+  </a>
 </div>
 `;
 
 exports[`expands more options 2`] = `
-<div
-  className="login-page"
-  id="login_form"
+<form
+  className="login-form"
+  onSubmit={[Function]}
 >
-  <h1
-    className="login-title text-center"
+  <Connect(GlobalMessages) />
+  <div
+    className="big-spacer-bottom"
   >
-    login.login_to_sonarqube
-  </h1>
-  <OAuthProviders
-    identityProviders={
-      Array [
-        Object {
-          "backgroundColor": "#000",
-          "iconPath": "/some/path",
-          "key": "foo",
-          "name": "foo",
-        },
-      ]
-    }
-    returnTo=""
-  />
-  <form
-    className="login-form"
-    onSubmit={[Function]}
+    <label
+      className="login-label"
+      htmlFor="login"
+    >
+      login
+    </label>
+    <input
+      autoFocus={true}
+      className="login-input"
+      id="login"
+      maxLength={255}
+      name="login"
+      onChange={[Function]}
+      placeholder="login"
+      required={true}
+      type="text"
+      value=""
+    />
+  </div>
+  <div
+    className="big-spacer-bottom"
   >
-    <Connect(GlobalMessages) />
-    <div
-      className="big-spacer-bottom"
+    <label
+      className="login-label"
+      htmlFor="password"
     >
-      <label
-        className="login-label"
-        htmlFor="login"
-      >
-        login
-      </label>
-      <input
-        autoFocus={true}
-        className="login-input"
-        id="login"
-        maxLength={255}
-        name="login"
-        onChange={[Function]}
-        placeholder="login"
-        required={true}
-        type="text"
-        value=""
-      />
-    </div>
+      password
+    </label>
+    <input
+      className="login-input"
+      id="password"
+      name="password"
+      onChange={[Function]}
+      placeholder="password"
+      required={true}
+      type="password"
+      value=""
+    />
+  </div>
+  <div>
     <div
-      className="big-spacer-bottom"
+      className="text-right overflow-hidden"
     >
-      <label
-        className="login-label"
-        htmlFor="password"
-      >
-        password
-      </label>
-      <input
-        className="login-input"
-        id="password"
-        name="password"
-        onChange={[Function]}
-        placeholder="password"
-        required={true}
-        type="password"
-        value=""
+      <DeferredSpinner
+        className="spacer-right"
+        loading={false}
+        timeout={100}
       />
-    </div>
-    <div>
-      <div
-        className="text-right overflow-hidden"
+      <SubmitButton
+        disabled={false}
       >
-        <DeferredSpinner
-          className="spacer-right"
-          loading={false}
-          timeout={100}
-        />
-        <SubmitButton
-          className=""
-        >
-          sessions.log_in
-        </SubmitButton>
-        <Link
-          className="spacer-left"
-          onlyActiveOnIndex={false}
-          style={Object {}}
-          to="/"
-        >
-          cancel
-        </Link>
-      </div>
+        sessions.log_in
+      </SubmitButton>
+      <Link
+        className="spacer-left"
+        onlyActiveOnIndex={false}
+        style={Object {}}
+        to="/"
+      >
+        cancel
+      </Link>
     </div>
-  </form>
-</div>
+  </div>
+</form>
 `;
 
-exports[`logs in with identity provider 1`] = `
-<div
-  className="login-page"
-  id="login_form"
+exports[`logs in with simple credentials 1`] = `
+<form
+  className="login-form"
+  onSubmit={[Function]}
 >
-  <h1
-    className="login-title text-center"
-  >
-    login.login_to_sonarqube
-  </h1>
-  <OAuthProviders
-    identityProviders={
-      Array [
-        Object {
-          "backgroundColor": "#000",
-          "iconPath": "/some/path",
-          "key": "foo",
-          "name": "foo",
-        },
-      ]
-    }
-    returnTo=""
-  />
+  <Connect(GlobalMessages) />
   <div
-    className="text-center"
+    className="big-spacer-bottom"
   >
-    <a
-      className="small text-muted js-more-options"
-      href="#"
-      onClick={[Function]}
+    <label
+      className="login-label"
+      htmlFor="login"
     >
-      login.more_options
-    </a>
+      login
+    </label>
+    <input
+      autoFocus={true}
+      className="login-input"
+      id="login"
+      maxLength={255}
+      name="login"
+      onChange={[Function]}
+      placeholder="login"
+      required={true}
+      type="text"
+      value=""
+    />
   </div>
-</div>
-`;
-
-exports[`logs in with simple credentials 1`] = `
-<div
-  className="login-page"
-  id="login_form"
->
-  <h1
-    className="login-title text-center"
-  >
-    login.login_to_sonarqube
-  </h1>
-  <form
-    className="login-form"
-    onSubmit={[Function]}
+  <div
+    className="big-spacer-bottom"
   >
-    <Connect(GlobalMessages) />
-    <div
-      className="big-spacer-bottom"
+    <label
+      className="login-label"
+      htmlFor="password"
     >
-      <label
-        className="login-label"
-        htmlFor="login"
-      >
-        login
-      </label>
-      <input
-        autoFocus={true}
-        className="login-input"
-        id="login"
-        maxLength={255}
-        name="login"
-        onChange={[Function]}
-        placeholder="login"
-        required={true}
-        type="text"
-        value=""
-      />
-    </div>
+      password
+    </label>
+    <input
+      className="login-input"
+      id="password"
+      name="password"
+      onChange={[Function]}
+      placeholder="password"
+      required={true}
+      type="password"
+      value=""
+    />
+  </div>
+  <div>
     <div
-      className="big-spacer-bottom"
+      className="text-right overflow-hidden"
     >
-      <label
-        className="login-label"
-        htmlFor="password"
-      >
-        password
-      </label>
-      <input
-        className="login-input"
-        id="password"
-        name="password"
-        onChange={[Function]}
-        placeholder="password"
-        required={true}
-        type="password"
-        value=""
+      <DeferredSpinner
+        className="spacer-right"
+        loading={false}
+        timeout={100}
       />
-    </div>
-    <div>
-      <div
-        className="text-right overflow-hidden"
+      <SubmitButton
+        disabled={false}
       >
-        <DeferredSpinner
-          className="spacer-right"
-          loading={false}
-          timeout={100}
-        />
-        <SubmitButton
-          className=""
-        >
-          sessions.log_in
-        </SubmitButton>
-        <Link
-          className="spacer-left"
-          onlyActiveOnIndex={false}
-          style={Object {}}
-          to="/"
-        >
-          cancel
-        </Link>
-      </div>
+        sessions.log_in
+      </SubmitButton>
+      <Link
+        className="spacer-left"
+        onlyActiveOnIndex={false}
+        style={Object {}}
+        to="/"
+      >
+        cancel
+      </Link>
     </div>
-  </form>
-</div>
+  </div>
+</form>
 `;
 
 exports[`should display a spinner and disabled button while loading 1`] = `
-<div
-  className="login-page"
-  id="login_form"
+<form
+  className="login-form"
+  onSubmit={[Function]}
 >
-  <h1
-    className="login-title text-center"
+  <Connect(GlobalMessages) />
+  <div
+    className="big-spacer-bottom"
   >
-    login.login_to_sonarqube
-  </h1>
-  <form
-    className="login-form"
-    onSubmit={[Function]}
+    <label
+      className="login-label"
+      htmlFor="login"
+    >
+      login
+    </label>
+    <input
+      autoFocus={true}
+      className="login-input"
+      id="login"
+      maxLength={255}
+      name="login"
+      onChange={[Function]}
+      placeholder="login"
+      required={true}
+      type="text"
+      value="admin"
+    />
+  </div>
+  <div
+    className="big-spacer-bottom"
   >
-    <Connect(GlobalMessages) />
-    <div
-      className="big-spacer-bottom"
+    <label
+      className="login-label"
+      htmlFor="password"
     >
-      <label
-        className="login-label"
-        htmlFor="login"
-      >
-        login
-      </label>
-      <input
-        autoFocus={true}
-        className="login-input"
-        id="login"
-        maxLength={255}
-        name="login"
-        onChange={[Function]}
-        placeholder="login"
-        required={true}
-        type="text"
-        value="admin"
-      />
-    </div>
+      password
+    </label>
+    <input
+      className="login-input"
+      id="password"
+      name="password"
+      onChange={[Function]}
+      placeholder="password"
+      required={true}
+      type="password"
+      value="admin"
+    />
+  </div>
+  <div>
     <div
-      className="big-spacer-bottom"
+      className="text-right overflow-hidden"
     >
-      <label
-        className="login-label"
-        htmlFor="password"
-      >
-        password
-      </label>
-      <input
-        className="login-input"
-        id="password"
-        name="password"
-        onChange={[Function]}
-        placeholder="password"
-        required={true}
-        type="password"
-        value="admin"
+      <DeferredSpinner
+        className="spacer-right"
+        loading={true}
+        timeout={100}
       />
-    </div>
-    <div>
-      <div
-        className="text-right overflow-hidden"
+      <SubmitButton
+        disabled={true}
       >
-        <DeferredSpinner
-          className="spacer-right"
-          loading={true}
-          timeout={100}
-        />
-        <SubmitButton
-          className="disabled"
-        >
-          sessions.log_in
-        </SubmitButton>
-        <Link
-          className="spacer-left"
-          onlyActiveOnIndex={false}
-          style={Object {}}
-          to="/"
-        >
-          cancel
-        </Link>
-      </div>
+        sessions.log_in
+      </SubmitButton>
+      <Link
+        className="spacer-left"
+        onlyActiveOnIndex={false}
+        style={Object {}}
+        to="/"
+      >
+        cancel
+      </Link>
     </div>
-  </form>
-</div>
+  </div>
+</form>
 `;
diff --git a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/LoginSonarCloud-test.tsx.snap b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/LoginSonarCloud-test.tsx.snap
new file mode 100644 (file)
index 0000000..a14d198
--- /dev/null
@@ -0,0 +1,39 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`logs in with identity provider 1`] = `
+<div
+  className="sonarcloud-login-page boxed-group boxed-group-inner"
+  id="login_form"
+>
+  <div
+    className="text-center"
+  >
+    <img
+      alt="SonarCloud logo"
+      height={36}
+      src="null/images/sc-icon.svg"
+      width={36}
+    />
+    <h1
+      className="sonarcloud-login-title"
+    >
+      login.login_or_signup_to_sonarcloud
+    </h1>
+  </div>
+  <OAuthProviders
+    className="sonarcloud-oauth-providers"
+    formatLabel={[Function]}
+    identityProviders={
+      Array [
+        Object {
+          "backgroundColor": "#000",
+          "iconPath": "/some/path",
+          "key": "foo",
+          "name": "foo",
+        },
+      ]
+    }
+    returnTo=""
+  />
+</div>
+`;
diff --git a/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/OAuthProviders-test.tsx.snap b/server/sonar-web/src/main/js/apps/sessions/components/__tests__/__snapshots__/OAuthProviders-test.tsx.snap
new file mode 100644 (file)
index 0000000..e7adf30
--- /dev/null
@@ -0,0 +1,99 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`should render correctly 1`] = `
+<section
+  className="oauth-providers"
+>
+  <ul>
+    <li
+      key="foo"
+    >
+      <a
+        href="/sessions/init/foo?return_to="
+        style={
+          Object {
+            "backgroundColor": "#000",
+            "color": "#fff",
+          }
+        }
+      >
+        <img
+          alt="Foo"
+          height="20"
+          src="/some/path"
+          width="20"
+        />
+        <span>
+          login.login_with_x.Foo
+        </span>
+      </a>
+    </li>
+    <li
+      key="bar"
+    >
+      <a
+        href="/sessions/init/bar?return_to="
+        style={
+          Object {
+            "backgroundColor": "#00F",
+            "color": "#fff",
+          }
+        }
+      >
+        <img
+          alt="Bar"
+          height="20"
+          src="/icon/path"
+          width="20"
+        />
+        <span>
+          login.login_with_x.Bar
+        </span>
+      </a>
+      <Tooltip
+        overlay="Help message!"
+      >
+        <div
+          className="oauth-providers-help"
+        >
+          <HelpIcon
+            fill="#4b9fd5"
+          />
+        </div>
+      </Tooltip>
+    </li>
+  </ul>
+</section>
+`;
+
+exports[`should use the custom label formatter 1`] = `
+<section
+  className="oauth-providers"
+>
+  <ul>
+    <li
+      key="foo"
+    >
+      <a
+        href="/sessions/init/foo?return_to="
+        style={
+          Object {
+            "backgroundColor": "#000",
+            "color": "#fff",
+          }
+        }
+      >
+        <img
+          alt="Foo"
+          height="20"
+          src="/some/path"
+          width="20"
+        />
+        <span>
+          custom_format.Foo
+        </span>
+      </a>
+    </li>
+  </ul>
+</section>
+`;
index 92b4d569909fd4425bafdfd7228e2ac4a3fcecb4..31b74819be8bbe5aec45e078cfd43dade5560fde 100644 (file)
@@ -23,7 +23,7 @@ const routes = [
   {
     path: 'new',
     getComponent(_: RouterState, callback: (err: any, component: RouteComponent) => any) {
-      import('./components/LoginFormContainer').then(i => callback(null, i.default));
+      import('./components/LoginContainer').then(i => callback(null, i.default));
     }
   },
   {
index d75b929ec5456e1c775eef6c5fc5c295d65c6a5c..8a5e326417635a66d2c6e9a6386ed0ae27fa72cd 100644 (file)
@@ -1380,9 +1380,10 @@ user.password_doesnt_match_confirmation=Password doesn't match confirmation.
 user.login_or_email_used_as_scm_account=Login and email are automatically considered as SCM accounts
 
 login.login_to_sonarqube=Log In to SonarQube
-login.login_to_sonarcloud=Log In to SonarCloud
-login.more_options=More options
+login.login_or_signup_to_sonarcloud=Log in or Sign up to SonarCloud
 login.login_with_x=Log in with {0}
+login.more_options=More options
+login.with_x=With {0}
 
 unauthorized.message=You're not authorized to access this page. Please contact the administrator.
 unauthorized.reason=Reason: