import it.settings.SettingsTest;
import it.settings.SettingsTestRestartingOrchestrator;
import it.settings.SubCategoriesTest;
+import it.user.MyAccountPageTest;
import org.junit.ClassRule;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
TimeMachineTest.class,
// action plan
ActionPlanTest.class,
- ActionPlanUiTest.class
+ ActionPlanUiTest.class,
+ MyAccountPageTest.class
})
public class Category1Suite {
import com.sonar.orchestrator.Orchestrator;
import com.sonar.orchestrator.selenium.Selenese;
import it.Category1Suite;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.ClassRule;
-import org.junit.Test;
-import org.sonar.wsclient.SonarClient;
-import org.sonar.wsclient.user.UserParameters;
+import org.junit.*;
+import org.sonarqube.ws.client.PostRequest;
+import org.sonarqube.ws.client.WsClient;
import util.selenium.SeleneseTest;
+import static util.ItUtils.newAdminWsClient;
+
public class MyAccountPageTest {
@ClassRule
public static Orchestrator orchestrator = Category1Suite.ORCHESTRATOR;
+ private static WsClient adminWsClient;
@BeforeClass
- public static void initUser() {
+ public static void setUp() {
+ adminWsClient = newAdminWsClient(orchestrator);
+ }
+
+ @Before
+ public void initUser() {
createUser("account-user", "User With Account", "user@example.com");
}
- @AfterClass
- public static void deleteTestUser() {
+ @After
+ public void deleteTestUser() {
deactivateUser("account-user");
}
new SeleneseTest(selenese).runOn(orchestrator);
}
+ @Test
+ public void should_change_password() throws Exception {
+ Selenese selenese = Selenese.builder().setHtmlTestsInClasspath("should_change_password",
+ "/user/MyAccountPageTest/should_change_password.html"
+ ).build();
+ new SeleneseTest(selenese).runOn(orchestrator);
+ }
+
private static void createUser(String login, String name, String email) {
- SonarClient client = orchestrator.getServer().adminWsClient();
- UserParameters userCreationParameters = UserParameters.create()
- .login(login)
- .name(name)
- .email(email)
- .password("password")
- .passwordConfirmation("password");
- client.userClient().create(userCreationParameters);
+ adminWsClient.wsConnector().call(
+ new PostRequest("api/users/create")
+ .setParam("login", login)
+ .setParam("name", name)
+ .setParam("email", email)
+ .setParam("password", "password"));
}
- private static void deactivateUser(String user) {
- orchestrator.getServer().adminWsClient().userClient().deactivate(user);
+ private static void deactivateUser(String login) {
+ adminWsClient.wsConnector().call(
+ new PostRequest("api/users/deactivate")
+ .setParam("login", login));
}
}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head profile="http://selenium-ide.openqa.org/profiles/test-case">
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
+ <link rel="selenium.base" href="http://localhost:49506"/>
+ <title>should_change_password</title>
+</head>
+<body>
+<table cellpadding="1" cellspacing="1" border="1">
+<thead>
+<tr>
+<td rowspan="1" colspan="3">should_change_password</td>
+</tr>
+</thead>
+<tbody>
+<tr>
+ <td>open</td>
+ <td>/sonar/sessions/login</td>
+ <td></td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>login</td>
+ <td>account-user</td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>password</td>
+ <td>password</td>
+</tr>
+<tr>
+ <td>clickAndWait</td>
+ <td>commit</td>
+ <td></td>
+</tr>
+<tr>
+ <td>open</td>
+ <td>/sonar/account/security</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>id=change-password</td>
+ <td></td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>id=change-password</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForElementPresent</td>
+ <td>id=old_password</td>
+ <td></td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>id=old_password</td>
+ <td>password</td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>id=password</td>
+ <td>new_password</td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>id=password_confirmation</td>
+ <td>new_password</td>
+</tr>
+<tr>
+ <td>click</td>
+ <td>commit</td>
+ <td></td>
+</tr>
+<tr>
+ <td>open</td>
+ <td>/sonar/sessions/logout</td>
+ <td></td>
+</tr>
+<tr>
+ <td>open</td>
+ <td>/sonar/sessions/login</td>
+ <td></td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>login</td>
+ <td>account-user</td>
+</tr>
+<tr>
+ <td>type</td>
+ <td>password</td>
+ <td>new_password</td>
+</tr>
+<tr>
+ <td>clickAndWait</td>
+ <td>commit</td>
+ <td></td>
+</tr>
+<tr>
+ <td>waitForText</td>
+ <td>id=global-navigation</td>
+ <td>*User With Account*</td>
+</tr>
+</tbody>
+</table>
+</body>
+</html>
import AccountApp from './containers/AccountApp';
import Home from './components/Home';
import NotificationsContainer from './containers/NotificationsContainer';
+import SecurityContainer from './containers/SecurityContainer';
window.sonarqube.appStarted.then(options => {
const el = document.querySelector(options.el);
<Route path="/" component={AccountApp}>
<IndexRoute component={Home}/>
<Route path="notifications" component={NotificationsContainer}/>
+ <Route path="security" component={SecurityContainer}/>
<Redirect from="/index" to="/"/>
</Route>
{translate('my_account.notifications')}
</IndexLink>
</li>
+ <li>
+ <IndexLink to="security" activeClassName="active">
+ {translate('my_account.security')}
+ </IndexLink>
+ </li>
</ul>
</div>
</nav>
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 React, { Component } from 'react';
+
+import ChangePasswordView from '../change-password-view';
+
+export default class Password extends Component {
+ handleChangePassword () {
+ new ChangePasswordView().render();
+ }
+
+ render () {
+ return (
+ <section>
+ <h2 className="spacer-bottom">Password</h2>
+ <button
+ id="change-password"
+ onClick={this.handleChangePassword.bind(this)}
+ type="submit">
+ Change Password
+ </button>
+ </section>
+ );
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 React from 'react';
+
+import Password from './Password';
+import Tokens from './Tokens';
+
+export default function Security ({ user }) {
+ return (
+ <div className="columns">
+ <div className="column-half">
+ <Tokens user={user}/>
+ </div>
+
+ {user.canChangePassword && (
+ <div className="column-half">
+ <Password/>
+ </div>
+ )}
+ </div>
+ );
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 Backbone from 'backbone';
+import React, { Component } from 'react';
+
+import TokensView from '../tokens-view';
+
+export default class Tokens extends Component {
+ componentDidMount () {
+ this.renderView();
+ }
+
+ componentWillUnmount () {
+ this.destroyView();
+ }
+
+ renderView () {
+ const account = new Backbone.Model({
+ id: this.props.user.login
+ });
+
+ this.tokensView = new TokensView({
+ el: this.refs.container,
+ model: account
+ }).render();
+ }
+
+ destroyView () {
+ if (this.destroyView) {
+ this.tokensView.destroy();
+ }
+ }
+
+ render () {
+ return <div ref="container"></div>;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact 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 React from 'react';
+
+import Security from './../components/Security';
+
+export default function SecurityContainer () {
+ const { user } = window.sonarqube;
+
+ return <Security user={user}/>;
+}
login: '<%= escape_javascript current_user.login -%>',
name: '<%= escape_javascript current_user.name -%>',
email: '<%= escape_javascript current_user.email -%>',
+ canChangePassword: <%= User.editable_password? ? 'true' : 'false' -%>,
groups: [
<% current_user.groups.sort.each do |group| -%>
'<%= escape_javascript group.name -%>',
my_account.favorite_measure_filters=Favorite Measure Filters
my_account.notifications=Notifications
my_account.no_project_notifications=You have not set project notifications yet.
+my_account.security=Security