aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-qa-util/src
diff options
context:
space:
mode:
authorSimon Brandhof <simon.brandhof@sonarsource.com>2017-11-09 17:39:52 +0100
committerEric Hartmann <hartmann.eric@gmail.Com>2017-11-14 13:10:17 +0100
commita0677e1efa4db51e563f74548c5ca54a24a17efc (patch)
tree5a24c3543269d9316c160ebbf826f5789dd8fa96 /server/sonar-qa-util/src
parentc0be4a24b1cf4f3f0fe8314e9e7c70d4cdc61b3c (diff)
downloadsonarqube-a0677e1efa4db51e563f74548c5ca54a24a17efc.tar.gz
sonarqube-a0677e1efa4db51e563f74548c5ca54a24a17efc.zip
Move helpers to sonar-qa-util
Diffstat (limited to 'server/sonar-qa-util/src')
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/Elasticsearch.java83
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/GroupTester.java102
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/LogsTailer.java200
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/OrganizationTester.java142
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/ProjectTester.java62
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/QGateTester.java108
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/QProfileTester.java129
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/SettingTester.java92
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/Tester.java242
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/TesterSession.java42
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/UserTester.java108
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/BackgroundTaskItem.java4
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/LoginPage.java2
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/MarketplacePage.java2
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/Navigation.java6
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/NotificationsPage.java8
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectActivityPage.java2
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectCodePage.java3
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectDashboardPage.java17
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectKeyPage.java4
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/SystemInfoPageItem.java6
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/issues/IssuesPage.java22
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/issues/package-info.java23
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/measures/MeasuresPage.java8
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/measures/package-info.java23
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/organization/MemberItem.java2
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/organization/MembersPage.java4
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/organization/package-info.java23
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/projects/ProjectsPage.java2
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/projects/package-info.java23
-rw-r--r--server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/settings/package-info.java23
31 files changed, 1469 insertions, 48 deletions
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/Elasticsearch.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/Elasticsearch.java
new file mode 100644
index 00000000000..434044cab41
--- /dev/null
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/Elasticsearch.java
@@ -0,0 +1,83 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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;
+
+import java.io.IOException;
+import java.net.InetAddress;
+import okhttp3.MediaType;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Helper to directly access Elasticsearch. It requires the HTTP port
+ * to be open.
+ */
+public class Elasticsearch {
+
+ private final int httpPort;
+
+ public Elasticsearch(int httpPort) {
+ this.httpPort = httpPort;
+ }
+
+ /**
+ * Forbid indexing requests on the specified index. Index becomes read-only.
+ */
+ public void lockWrites(String index) throws IOException {
+ putIndexSetting(httpPort, index, "blocks.write", "true");
+ }
+
+ /**
+ * Enable indexing requests on the specified index.
+ * @see #lockWrites(String)
+ */
+ public void unlockWrites(String index) throws IOException {
+ putIndexSetting(httpPort, index, "blocks.write", "false");
+ }
+
+ public void makeYellow() throws IOException {
+ putIndexSetting(httpPort, "issues", "number_of_replicas", "5");
+ }
+
+ public void makeGreen() throws IOException {
+ putIndexSetting(httpPort, "issues", "number_of_replicas", "0");
+ }
+
+ private static void putIndexSetting(int searchHttpPort, String index, String key, String value) throws IOException {
+ Request.Builder request = new Request.Builder()
+ .url(baseUrl(searchHttpPort) + index + "/_settings")
+ .put(RequestBody.create(MediaType.parse("application/json"), "{" +
+ " \"index\" : {" +
+ " \"" + key + "\" : \"" + value + "\"" +
+ " }" +
+ "}"));
+ OkHttpClient okClient = new OkHttpClient.Builder().build();
+ Response response = okClient.newCall(request.build()).execute();
+ assertThat(response.isSuccessful()).isTrue();
+ }
+
+ private static String baseUrl(int searchHttpPort) {
+ return "http://" + InetAddress.getLoopbackAddress().getHostAddress() + ":" + searchHttpPort + "/";
+ }
+}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/GroupTester.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/GroupTester.java
new file mode 100644
index 00000000000..8b395c90518
--- /dev/null
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/GroupTester.java
@@ -0,0 +1,102 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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;
+
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+import org.sonarqube.ws.Organizations;
+import org.sonarqube.ws.WsUserGroups;
+import org.sonarqube.ws.WsUsers;
+import org.sonarqube.ws.WsUsers.GroupsWsResponse.Group;
+import org.sonarqube.ws.client.user.GroupsRequest;
+import org.sonarqube.ws.client.usergroup.AddUserWsRequest;
+import org.sonarqube.ws.client.usergroup.CreateWsRequest;
+
+import static java.util.Arrays.stream;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class GroupTester {
+
+ private static final AtomicInteger ID_GENERATOR = new AtomicInteger();
+
+ private final TesterSession session;
+
+ GroupTester(TesterSession session) {
+ this.session = session;
+ }
+
+ @SafeVarargs
+ public final WsUserGroups.Group generate(@Nullable Organizations.Organization organization, Consumer<CreateWsRequest.Builder>... populators) {
+ int id = ID_GENERATOR.getAndIncrement();
+ CreateWsRequest.Builder request = CreateWsRequest.builder()
+ .setName("Group" + id)
+ .setDescription("Description " + id)
+ .setOrganization(organization != null ? organization.getKey() : null);
+ stream(populators).forEach(p -> p.accept(request));
+ return session.wsClient().userGroups().create(request.build()).getGroup();
+ }
+
+ public List<Group> getGroupsOfUser(@Nullable Organizations.Organization organization, String userLogin) {
+ GroupsRequest request = GroupsRequest.builder()
+ .setOrganization(organization != null ? organization.getKey() : null)
+ .setLogin(userLogin)
+ .build();
+ WsUsers.GroupsWsResponse response = session.users().service().groups(request);
+ return response.getGroupsList();
+ }
+
+ public GroupTester addMemberToGroups(Organizations.Organization organization, String userLogin, String... groups) {
+ for (String group : groups) {
+ AddUserWsRequest request = AddUserWsRequest.builder()
+ .setLogin(userLogin)
+ .setOrganization(organization.getKey())
+ .setName(group)
+ .build();
+ session.wsClient().userGroups().addUser(request);
+ }
+ return this;
+ }
+
+ public GroupTester assertThatUserIsMemberOf(@Nullable Organizations.Organization organization, String userLogin, String expectedGroup, String... otherExpectedGroups) {
+ List<String> groups = getGroupsOfUser(organization, userLogin)
+ .stream()
+ .map(Group::getName)
+ .collect(Collectors.toList());
+
+ assertThat(groups).contains(expectedGroup);
+ if (otherExpectedGroups.length > 0) {
+ assertThat(groups).contains(otherExpectedGroups);
+ }
+ return this;
+ }
+
+ public GroupTester assertThatUserIsOnlyMemberOf(@Nullable Organizations.Organization organization, String userLogin, String... expectedGroups) {
+ Set<String> groups = getGroupsOfUser(organization, userLogin)
+ .stream()
+ .map(Group::getName)
+ .collect(Collectors.toSet());
+ assertThat(groups).containsExactlyInAnyOrder(expectedGroups);
+ return this;
+ }
+}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/LogsTailer.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/LogsTailer.java
new file mode 100644
index 00000000000..a172b56a649
--- /dev/null
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/LogsTailer.java
@@ -0,0 +1,200 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.function.Consumer;
+import java.util.regex.Pattern;
+import java.util.stream.Collectors;
+import org.apache.commons.io.input.Tailer;
+import org.apache.commons.io.input.TailerListenerAdapter;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Watch log files, usually server logs (see Orchestrator.getServer().get*Logs()).
+ * This class allows to not load the full content in memory.
+ */
+public class LogsTailer implements AutoCloseable {
+
+ private final List<Tailer> tailers;
+ private final LogConsumer logConsumer;
+
+ private LogsTailer(Builder builder) {
+ logConsumer = new LogConsumer(builder.consumers);
+ tailers = builder.files.stream()
+ .map(file -> Tailer.create(file, logConsumer, 500))
+ .collect(Collectors.toList());
+ }
+
+ public Watch watch(String text) {
+ return new Watch(text);
+ }
+
+ @Override
+ public void close() {
+ for (Tailer tailer : tailers) {
+ tailer.stop();
+ }
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static class Builder {
+ private final List<File> files = new ArrayList<>();
+ private final List<Consumer<String>> consumers = new ArrayList<>();
+
+ public Builder addFile(File file) {
+ this.files.add(file);
+ return this;
+ }
+
+ public Builder addFiles(File file, File... otherFiles) {
+ this.files.add(file);
+ Collections.addAll(this.files, otherFiles);
+ return this;
+ }
+
+ /**
+ * Adds a consumer that is called on each new line appended
+ * to the files.
+ * Note that the consumer {@link Content} allows to keep
+ * all past logs in memory.
+ */
+ public Builder addConsumer(Consumer<String> consumer) {
+ this.consumers.add(consumer);
+ return this;
+ }
+
+ public LogsTailer build() {
+ return new LogsTailer(this);
+ }
+ }
+
+ private static class LogConsumer extends TailerListenerAdapter {
+ private final List<Consumer<String>> consumers = Collections.synchronizedList(new ArrayList<>());
+
+ private LogConsumer(List<Consumer<String>> consumers) {
+ this.consumers.addAll(consumers);
+ }
+
+ @Override
+ public void handle(String line) {
+ synchronized (consumers) {
+ for (Consumer<String> consumer : consumers) {
+ try {
+ consumer.accept(line);
+ } catch (Exception e) {
+ // do not prevent other consumers to handle the log
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ public void add(Consumer<String> consumer) {
+ this.consumers.add(consumer);
+ }
+
+ public void remove(Consumer<String> consumer) {
+ this.consumers.remove(consumer);
+ }
+ }
+
+ public class Watch implements AutoCloseable {
+ private final String expectedText;
+ private final CountDownLatch foundSignal = new CountDownLatch(1);
+ private String log = null;
+ private final Consumer<String> consumer;
+
+ private Watch(String expectedText) {
+ this.expectedText = requireNonNull(expectedText);
+ this.consumer = l -> {
+ if (l.contains(this.expectedText)) {
+ this.log = l;
+ foundSignal.countDown();
+ }
+ };
+ logConsumer.add(consumer);
+ }
+
+ /**
+ * Blocks until the expected log appears in watched files.
+ */
+ public void waitForLog() throws InterruptedException {
+ foundSignal.await();
+ }
+
+ /**
+ * Blocks until the expected log appears in watched files with timeout
+ */
+ public void waitForLog(long timeout, TimeUnit timeUnit) throws InterruptedException {
+ foundSignal.await(timeout, timeUnit);
+ }
+
+ public Optional<String> getLog() {
+ return Optional.ofNullable(log);
+ }
+
+ @Override
+ public void close() {
+ logConsumer.remove(consumer);
+ }
+ }
+
+ public static class Content implements Consumer<String> {
+ private final List<String> lines = Collections.synchronizedList(new ArrayList<>());
+
+ @Override
+ public void accept(String s) {
+ lines.add(s);
+ }
+
+ public boolean hasText(String text) {
+ synchronized (lines) {
+ for (String line : lines) {
+ if (line.contains(text)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ public boolean hasLineMatching(Pattern pattern) {
+ synchronized (lines) {
+ for (String line : lines) {
+ if (pattern.matcher(line).matches()) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/OrganizationTester.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/OrganizationTester.java
new file mode 100644
index 00000000000..a9d098ee261
--- /dev/null
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/OrganizationTester.java
@@ -0,0 +1,142 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import javax.annotation.Nullable;
+import org.assertj.core.api.Assertions;
+import org.sonarqube.ws.Organizations;
+import org.sonarqube.ws.WsUsers;
+import org.sonarqube.ws.client.HttpException;
+import org.sonarqube.ws.client.PostRequest;
+import org.sonarqube.ws.client.organization.CreateWsRequest;
+import org.sonarqube.ws.client.organization.OrganizationService;
+import org.sonarqube.ws.client.organization.SearchMembersWsRequest;
+import org.sonarqube.ws.client.organization.SearchWsRequest;
+import org.sonarqube.ws.client.user.GroupsRequest;
+
+import static java.util.Arrays.stream;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class OrganizationTester {
+
+ private static final AtomicInteger ID_GENERATOR = new AtomicInteger();
+
+ private final TesterSession session;
+
+ OrganizationTester(TesterSession session) {
+ this.session = session;
+ }
+
+ void enableSupport() {
+ session.wsClient().wsConnector().call(new PostRequest("api/organizations/enable_support"));
+ }
+
+ void deleteNonGuardedOrganizations() {
+ service().search(SearchWsRequest.builder().build()).getOrganizationsList()
+ .stream()
+ .filter(o -> !o.getKey().equals("default-organization"))
+ .forEach(organization -> service().delete(organization.getKey()));
+ }
+
+ @SafeVarargs
+ public final Organizations.Organization generate(Consumer<CreateWsRequest.Builder>... populators) {
+ int id = ID_GENERATOR.getAndIncrement();
+ CreateWsRequest.Builder request = new CreateWsRequest.Builder()
+ .setKey("org" + id)
+ .setName("Org " + id)
+ .setDescription("Description " + id)
+ .setUrl("http://test" + id);
+ stream(populators).forEach(p -> p.accept(request));
+ return service().create(request.build()).getOrganization();
+ }
+
+ public OrganizationTester addMember(Organizations.Organization organization, WsUsers.CreateWsResponse.User user) {
+ service().addMember(organization.getKey(), user.getLogin());
+ return this;
+ }
+
+ public Organizations.Organization getDefaultOrganization() {
+ return service().search(SearchWsRequest.builder().build()).getOrganizationsList()
+ .stream()
+ .filter(o -> o.getKey().equals("default-organization"))
+ .findFirst().orElseThrow(() -> new IllegalStateException("Can't find default organization"));
+ }
+
+ public OrganizationTester assertThatOrganizationDoesNotExist(String organizationKey) {
+ SearchWsRequest request = new SearchWsRequest.Builder().setOrganizations(organizationKey).build();
+ Organizations.SearchWsResponse searchWsResponse = service().search(request);
+ Assertions.assertThat(searchWsResponse.getOrganizationsList()).isEmpty();
+ return this;
+ }
+
+ public OrganizationTester assertThatMemberOf(Organizations.Organization organization, WsUsers.CreateWsResponse.User user) {
+ return assertThatMemberOf(organization, user.getLogin());
+ }
+
+ public OrganizationTester assertThatMemberOf(Organizations.Organization organization, String userLogin) {
+ verifyOrganizationMembership(organization, userLogin, true);
+ verifyMembersGroupMembership(userLogin, organization, true);
+ return this;
+ }
+
+ public OrganizationTester assertThatNotMemberOf(Organizations.Organization organization, WsUsers.CreateWsResponse.User user) {
+ return assertThatNotMemberOf(organization, user.getLogin());
+ }
+
+ public OrganizationTester assertThatNotMemberOf(Organizations.Organization organization, String userLogin) {
+ verifyOrganizationMembership(organization, userLogin, false);
+ try {
+ verifyMembersGroupMembership(userLogin, organization, false);
+ } catch (HttpException e) {
+ // do not fail if user does not exist
+ if (e.code() != 404) {
+ throw e;
+ }
+ }
+ return this;
+ }
+
+ private void verifyOrganizationMembership(@Nullable Organizations.Organization organization, String userLogin, boolean isMember) {
+ List<Organizations.User> users = service().searchMembers(new SearchMembersWsRequest()
+ .setQuery(userLogin)
+ .setSelected("selected")
+ .setOrganization(organization != null ? organization.getKey() : null))
+ .getUsersList();
+ Assertions.assertThat(users).hasSize(isMember ? 1 : 0);
+ }
+
+ private void verifyMembersGroupMembership(String userLogin, @Nullable Organizations.Organization organization, boolean isMember) {
+ List<WsUsers.GroupsWsResponse.Group> groups = session.wsClient().users().groups(GroupsRequest.builder()
+ .setLogin(userLogin)
+ .setOrganization(organization != null ? organization.getKey() : null)
+ .setQuery("Members")
+ .setSelected("selected")
+ .build())
+ .getGroupsList();
+ Assertions.assertThat(groups).hasSize(isMember ? 1 : 0);
+ }
+
+ public OrganizationService service() {
+ return session.wsClient().organizations();
+ }
+}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/ProjectTester.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/ProjectTester.java
new file mode 100644
index 00000000000..6b63e850ee2
--- /dev/null
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/ProjectTester.java
@@ -0,0 +1,62 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import javax.annotation.Nullable;
+import org.sonarqube.ws.Organizations;
+import org.sonarqube.ws.WsProjects;
+import org.sonarqube.ws.client.project.CreateRequest;
+import org.sonarqube.ws.client.project.DeleteRequest;
+import org.sonarqube.ws.client.project.ProjectsService;
+import org.sonarqube.ws.client.project.SearchWsRequest;
+
+import static java.util.Arrays.stream;
+import static java.util.Collections.singletonList;
+
+public class ProjectTester {
+
+ private static final AtomicInteger ID_GENERATOR = new AtomicInteger();
+
+ private final TesterSession session;
+
+ ProjectTester(TesterSession session) {
+ this.session = session;
+ }
+
+ void deleteAll() {
+ ProjectsService service = session.wsClient().projects();
+ service.search(SearchWsRequest.builder().setQualifiers(singletonList("TRK")).build()).getComponentsList().forEach(p ->
+ service.delete(DeleteRequest.builder().setKey(p.getKey()).build()));
+ }
+
+ @SafeVarargs
+ public final WsProjects.CreateWsResponse.Project generate(@Nullable Organizations.Organization organization, Consumer<CreateRequest.Builder>... populators) {
+ int id = ID_GENERATOR.getAndIncrement();
+ CreateRequest.Builder request = CreateRequest.builder()
+ .setKey("key" + id)
+ .setName("Name " + id)
+ .setOrganization(organization != null ? organization.getKey() : null);
+ stream(populators).forEach(p -> p.accept(request));
+
+ return session.wsClient().projects().create(request.build()).getProject();
+ }
+}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/QGateTester.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/QGateTester.java
new file mode 100644
index 00000000000..e0a1f8bae53
--- /dev/null
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/QGateTester.java
@@ -0,0 +1,108 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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;
+
+import com.google.gson.Gson;
+import com.google.gson.annotations.SerializedName;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.sonarqube.ws.WsProjects.CreateWsResponse.Project;
+import org.sonarqube.ws.WsQualityGates.CreateWsResponse;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.PostRequest;
+import org.sonarqube.ws.client.qualitygate.QualityGatesService;
+import org.sonarqube.ws.client.qualitygate.SelectWsRequest;
+
+public class QGateTester {
+ private static final AtomicInteger ID_GENERATOR = new AtomicInteger();
+
+ private final TesterSession session;
+
+ QGateTester(TesterSession session) {
+ this.session = session;
+ }
+
+ public QualityGatesService service() {
+ return session.wsClient().qualityGates();
+ }
+
+ void deleteAll() {
+ String json = session.wsClient().wsConnector().call(new GetRequest("api/qualitygates/list")).failIfNotSuccessful().content();
+ ListResponse response = ListResponse.parse(json);
+ response.getQualityGates().stream()
+ .filter(qualityGate -> !qualityGate.getId().equals(response.getDefault()))
+ .forEach(qualityGate -> session.wsClient().wsConnector().call(new PostRequest("api/qualitygates/destroy").setParam("id", qualityGate.getId())).failIfNotSuccessful());
+ }
+
+ public CreateWsResponse generate() {
+ int id = ID_GENERATOR.getAndIncrement();
+ return session.wsClient().qualityGates().create("QualityGate" + id);
+ }
+
+ public void associateProject(CreateWsResponse qualityGate, Project project){
+ service().associateProject(new SelectWsRequest().setGateId(qualityGate.getId()).setProjectKey(project.getKey()));
+ }
+
+ public static class ListResponse {
+
+ @SerializedName("default")
+ private final String defaultQGate;
+ @SerializedName("qualitygates")
+ private final List<QGate> qualityGates = new ArrayList<>();
+
+ public ListResponse(String defaultQGate) {
+ this.defaultQGate = defaultQGate;
+ }
+
+ public List<QGate> getQualityGates() {
+ return qualityGates;
+ }
+
+ public String getDefault() {
+ return defaultQGate;
+ }
+
+ public static ListResponse parse(String json) {
+ Gson gson = new Gson();
+ return gson.fromJson(json, ListResponse.class);
+ }
+
+ public static class QGate {
+ @SerializedName("id")
+ private final String id;
+ @SerializedName("name")
+ private final String name;
+
+ public QGate(String id, String name) {
+ this.id = id;
+ this.name = name;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+ }
+}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/QProfileTester.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/QProfileTester.java
new file mode 100644
index 00000000000..2c80d28fe2c
--- /dev/null
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/QProfileTester.java
@@ -0,0 +1,129 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import org.assertj.core.api.Assertions;
+import org.sonarqube.ws.Common;
+import org.sonarqube.ws.Organizations.Organization;
+import org.sonarqube.ws.QualityProfiles.CreateWsResponse.QualityProfile;
+import org.sonarqube.ws.Rules;
+import org.sonarqube.ws.WsProjects.CreateWsResponse.Project;
+import org.sonarqube.ws.client.HttpException;
+import org.sonarqube.ws.client.qualityprofile.ActivateRuleWsRequest;
+import org.sonarqube.ws.client.qualityprofile.AddProjectRequest;
+import org.sonarqube.ws.client.qualityprofile.CreateRequest;
+import org.sonarqube.ws.client.qualityprofile.QualityProfilesService;
+import org.sonarqube.ws.client.rule.SearchWsRequest;
+
+import static java.util.Arrays.asList;
+import static java.util.Arrays.stream;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class QProfileTester {
+ private static final AtomicInteger ID_GENERATOR = new AtomicInteger();
+
+ private final TesterSession session;
+
+ QProfileTester(TesterSession session) {
+ this.session = session;
+ }
+
+ public QualityProfilesService service() {
+ return session.wsClient().qualityProfiles();
+ }
+
+ @SafeVarargs
+ public final QualityProfile createXooProfile(Organization organization, Consumer<CreateRequest.Builder>... populators) {
+ int id = ID_GENERATOR.getAndIncrement();
+ CreateRequest.Builder request = CreateRequest.builder()
+ .setOrganizationKey(organization.getKey())
+ .setLanguage("xoo")
+ .setName("Profile" + id);
+ stream(populators).forEach(p -> p.accept(request));
+ return service().create(request.build()).getProfile();
+ }
+
+ public QProfileTester activateRule(QualityProfile profile, String ruleKey) {
+ return activateRule(profile.getKey(), ruleKey);
+ }
+
+ public QProfileTester activateRule(String profileKey, String ruleKey) {
+ ActivateRuleWsRequest request = ActivateRuleWsRequest.builder()
+ .setKey(profileKey)
+ .setRuleKey(ruleKey)
+ .build();
+ service().activateRule(request);
+ return this;
+ }
+
+ public QProfileTester deactivateRule(QualityProfile profile, String ruleKey) {
+ service().deactivateRule(profile.getKey(), ruleKey);
+ return this;
+ }
+
+ public QProfileTester assignQProfileToProject(QualityProfile profile, Project project) {
+ service().addProject(AddProjectRequest.builder()
+ .setProjectKey(project.getKey())
+ .setKey(profile.getKey())
+ .build());
+ return this;
+ }
+
+ public QProfileTester assertThatNumberOfActiveRulesEqualsTo(QualityProfile profile, int expectedActiveRules) {
+ return assertThatNumberOfActiveRulesEqualsTo(profile.getKey(), expectedActiveRules);
+ }
+
+ public QProfileTester assertThatNumberOfActiveRulesEqualsTo(String profileKey, int expectedActiveRules) {
+ try {
+ List<String> facetIds = asList("active_severities", "repositories", "languages", "severities", "statuses", "types");
+ SearchWsRequest request = new SearchWsRequest()
+ .setQProfile(profileKey)
+ .setActivation(true)
+ .setFacets(facetIds)
+ .setFields(singletonList("actives"));
+ Rules.SearchResponse response = session.wsClient().rules().search(request);
+
+ // assume that expectedActiveRules fits in first page of results
+ Assertions.assertThat(response.getRulesCount()).isEqualTo(expectedActiveRules);
+ Assertions.assertThat(response.getTotal()).isEqualTo(expectedActiveRules);
+ Assertions.assertThat(response.getActives().getActives()).as(response.toString()).hasSize(expectedActiveRules);
+
+ // verify facets
+ Assertions.assertThat(response.getFacets().getFacetsCount()).isEqualTo(facetIds.size());
+ response.getFacets().getFacetsList().forEach(facet -> {
+ long total = facet.getValuesList().stream()
+ .mapToLong(Common.FacetValue::getCount)
+ .sum();
+ assertThat(total).as("Facet " + facet.getProperty()).isEqualTo((long) expectedActiveRules);
+ });
+ } catch (HttpException e) {
+ if (expectedActiveRules == 0 && e.code() == 404) {
+ // profile does not exist, do nothing
+ return this;
+ }
+ throw e;
+ }
+ return this;
+ }
+}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/SettingTester.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/SettingTester.java
new file mode 100644
index 00000000000..00c08c10480
--- /dev/null
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/SettingTester.java
@@ -0,0 +1,92 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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;
+
+import com.google.common.collect.ImmutableSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import javax.annotation.Nullable;
+import org.sonarqube.ws.Settings;
+import org.sonarqube.ws.client.setting.ListDefinitionsRequest;
+import org.sonarqube.ws.client.setting.ResetRequest;
+import org.sonarqube.ws.client.setting.SetRequest;
+import org.sonarqube.ws.client.setting.SettingsService;
+
+public class SettingTester {
+
+ private static final Set<String> EMAIL_SETTINGS = ImmutableSet.of("email.smtp_host.secured", "email.smtp_port.secured", "email.smtp_secure_connection.secured",
+ "email.smtp_username.secured", "email.smtp_password.secured", "email.from", "email.prefix");
+
+ private final TesterSession session;
+
+ SettingTester(TesterSession session) {
+ this.session = session;
+ }
+
+ public SettingsService service() {
+ return session.wsClient().settings();
+ }
+
+ void deleteAll() {
+ List<String> settingKeys = Stream.concat(
+ session.wsClient().settings().listDefinitions(ListDefinitionsRequest.builder().build()).getDefinitionsList()
+ .stream()
+ .filter(def -> def.getType() != Settings.Type.LICENSE)
+ .map(Settings.Definition::getKey),
+ EMAIL_SETTINGS.stream())
+ .collect(Collectors.toList());
+ session.wsClient().settings().reset(ResetRequest.builder().setKeys(settingKeys).build());
+ }
+
+ public void resetSettings(String... keys){
+ session.wsClient().settings().reset(ResetRequest.builder().setKeys(keys).build());
+ }
+
+ public void setGlobalSetting(String key, @Nullable String value) {
+ setSetting(null, key, value);
+ }
+
+ public void setGlobalSettings(String... properties) {
+ for (int i = 0; i < properties.length; i += 2) {
+ setSetting(null, properties[i], properties[i + 1]);
+ }
+ }
+
+ public void setProjectSetting(String componentKey, String key, @Nullable String value) {
+ setSetting(componentKey, key, value);
+ }
+
+ public void setProjectSettings(String componentKey, String... properties) {
+ for (int i = 0; i < properties.length; i += 2) {
+ setSetting(componentKey, properties[i], properties[i + 1]);
+ }
+ }
+
+ private void setSetting(@Nullable String componentKey, String key, @Nullable String value) {
+ if (value == null) {
+ session.wsClient().settings().reset(ResetRequest.builder().setKeys(key).setComponent(componentKey).build());
+ } else {
+ session.wsClient().settings().set(SetRequest.builder().setKey(key).setValue(value).setComponent(componentKey).build());
+ }
+ }
+
+}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/Tester.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/Tester.java
new file mode 100644
index 00000000000..50412d8bc50
--- /dev/null
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/Tester.java
@@ -0,0 +1,242 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.container.Server;
+import javax.annotation.Nullable;
+import org.apache.commons.lang.StringUtils;
+import org.junit.rules.ExternalResource;
+import org.sonarqube.qa.util.pageobjects.Navigation;
+import org.sonarqube.ws.client.HttpConnector;
+import org.sonarqube.ws.client.WsClient;
+import org.sonarqube.ws.client.WsClientFactories;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * This JUnit rule wraps an {@link Orchestrator} instance and provides :
+ * <ul>
+ * <li>enabling the organization feature by default</li>
+ * <li>clean-up of organizations between tests</li>
+ * <li>clean-up of users between tests</li>
+ * <li>clean-up of session when opening a browser (cookies, local storage)</li>
+ * <li>quick access to {@link WsClient} instances</li>
+ * <li>helpers to generate organizations and users</li>
+ * </ul>
+ * <p>
+ * Recommendation is to define a {@code @Rule} instance. If not possible, then
+ * {@code @ClassRule} must be used through a {@link org.junit.rules.RuleChain}
+ * around {@link Orchestrator}.
+ */
+public class Tester extends ExternalResource implements TesterSession {
+
+ private final Orchestrator orchestrator;
+
+ // configuration before startup
+ private boolean disableOrganizations = false;
+ private Elasticsearch elasticsearch = null;
+
+ // initialized in #before()
+ private boolean beforeCalled = false;
+ private TesterSession rootSession;
+
+ public Tester(Orchestrator orchestrator) {
+ this.orchestrator = orchestrator;
+ String elasticsearchHttpPort = orchestrator.getDistribution().getServerProperty("sonar.search.httpPort");
+ if (StringUtils.isNotBlank(elasticsearchHttpPort)) {
+ this.elasticsearch = new Elasticsearch(Integer.parseInt(elasticsearchHttpPort));
+ }
+ }
+
+ public Tester disableOrganizations() {
+ verifyNotStarted();
+ disableOrganizations = true;
+ return this;
+ }
+
+ /**
+ * Enables Elasticsearch debugging, see {@link #elasticsearch()}.
+ * <p>
+ * The property "sonar.search.httpPort" must be defined before
+ * starting SonarQube server.
+ */
+ public Tester setElasticsearchHttpPort(int port) {
+ verifyNotStarted();
+ elasticsearch = new Elasticsearch(port);
+ return this;
+ }
+
+ @Override
+ public void before() {
+ verifyNotStarted();
+ rootSession = new TesterSessionImpl(orchestrator, "admin", "admin");
+
+ if (!disableOrganizations) {
+ organizations().enableSupport();
+ }
+
+ beforeCalled = true;
+ }
+
+ @Override
+ public void after() {
+ if (!disableOrganizations) {
+ organizations().deleteNonGuardedOrganizations();
+ }
+ users().deleteAll();
+ projects().deleteAll();
+ settings().deleteAll();
+ qGates().deleteAll();
+ }
+
+ public TesterSession asAnonymous() {
+ return as(null, null);
+ }
+
+ public TesterSession as(String login) {
+ return as(login, login);
+ }
+
+ public TesterSession as(String login, String password) {
+ verifyStarted();
+ return new TesterSessionImpl(orchestrator, login, password);
+ }
+
+ public Elasticsearch elasticsearch() {
+ return requireNonNull(elasticsearch, "Elasticsearch HTTP port is not defined. See #setElasticsearchHttpPort()");
+ }
+
+ /**
+ * Open a new browser session. Cookies are deleted.
+ */
+ public Navigation openBrowser() {
+ verifyStarted();
+ return Navigation.create(orchestrator);
+ }
+
+ private void verifyNotStarted() {
+ if (beforeCalled) {
+ throw new IllegalStateException("Orchestrator should not be already started");
+ }
+ }
+
+ private void verifyStarted() {
+ if (!beforeCalled) {
+ throw new IllegalStateException("Orchestrator is not started yet");
+ }
+ }
+
+ /**
+ * Web service client configured with root access
+ */
+ @Override
+ public WsClient wsClient() {
+ verifyStarted();
+ return rootSession.wsClient();
+ }
+
+ @Override
+ public GroupTester groups() {
+ return rootSession.groups();
+ }
+
+ @Override
+ public OrganizationTester organizations() {
+ return rootSession.organizations();
+ }
+
+ @Override
+ public ProjectTester projects() {
+ return rootSession.projects();
+ }
+
+ @Override
+ public QProfileTester qProfiles() {
+ return rootSession.qProfiles();
+ }
+
+ @Override
+ public UserTester users() {
+ return rootSession.users();
+ }
+
+ @Override
+ public SettingTester settings() {
+ return rootSession.settings();
+ }
+
+ @Override
+ public QGateTester qGates() {
+ return rootSession.qGates();
+ }
+
+ private static class TesterSessionImpl implements TesterSession {
+ private final WsClient client;
+
+ private TesterSessionImpl(Orchestrator orchestrator, @Nullable String login, @Nullable String password) {
+ Server server = orchestrator.getServer();
+ this.client = WsClientFactories.getDefault().newClient(HttpConnector.newBuilder()
+ .url(server.getUrl())
+ .credentials(login, password)
+ .build());
+ }
+
+ @Override
+ public WsClient wsClient() {
+ return client;
+ }
+
+ @Override
+ public GroupTester groups() {
+ return new GroupTester(this);
+ }
+
+ @Override
+ public OrganizationTester organizations() {
+ return new OrganizationTester(this);
+ }
+
+ @Override
+ public ProjectTester projects() {
+ return new ProjectTester(this);
+ }
+
+ @Override
+ public QProfileTester qProfiles() {
+ return new QProfileTester(this);
+ }
+
+ @Override
+ public UserTester users() {
+ return new UserTester(this);
+ }
+
+ @Override
+ public SettingTester settings() {
+ return new SettingTester(this);
+ }
+
+ @Override
+ public QGateTester qGates() {
+ return new QGateTester(this);
+ }
+ }
+}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/TesterSession.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/TesterSession.java
new file mode 100644
index 00000000000..6c2673abcb2
--- /dev/null
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/TesterSession.java
@@ -0,0 +1,42 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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;
+
+import org.sonarqube.ws.client.WsClient;
+
+public interface TesterSession {
+
+ WsClient wsClient();
+
+ GroupTester groups();
+
+ OrganizationTester organizations();
+
+ ProjectTester projects();
+
+ QProfileTester qProfiles();
+
+ UserTester users();
+
+ SettingTester settings();
+
+ QGateTester qGates();
+
+}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/UserTester.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/UserTester.java
new file mode 100644
index 00000000000..5f4f4a943d0
--- /dev/null
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/UserTester.java
@@ -0,0 +1,108 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.function.Consumer;
+import org.sonarqube.ws.Organizations;
+import org.sonarqube.ws.WsUsers;
+import org.sonarqube.ws.WsUsers.CreateWsResponse.User;
+import org.sonarqube.ws.client.PostRequest;
+import org.sonarqube.ws.client.user.CreateRequest;
+import org.sonarqube.ws.client.user.SearchRequest;
+import org.sonarqube.ws.client.user.UsersService;
+import org.sonarqube.ws.client.usergroup.AddUserWsRequest;
+
+import static java.util.Arrays.stream;
+
+public class UserTester {
+
+ private static final AtomicInteger ID_GENERATOR = new AtomicInteger();
+
+ private final TesterSession session;
+
+ UserTester(TesterSession session) {
+ this.session = session;
+ }
+
+ void deleteAll() {
+ session.wsClient().users().search(SearchRequest.builder().build()).getUsersList()
+ .stream()
+ .filter(u -> !"admin".equals(u.getLogin()))
+ .forEach(u -> {
+ PostRequest request = new PostRequest("api/users/deactivate").setParam("login", u.getLogin());
+ session.wsClient().wsConnector().call(request);
+ });
+ }
+
+ @SafeVarargs
+ public final User generate(Consumer<CreateRequest.Builder>... populators) {
+ int id = ID_GENERATOR.getAndIncrement();
+ String login = "login" + id;
+ CreateRequest.Builder request = CreateRequest.builder()
+ .setLogin(login)
+ .setPassword(login)
+ .setName("name" + id)
+ .setEmail(id + "@test.com");
+ stream(populators).forEach(p -> p.accept(request));
+ return service().create(request.build()).getUser();
+ }
+
+ @SafeVarargs
+ public final User generateAdministrator(Consumer<CreateRequest.Builder>... populators) {
+ User user = generate(populators);
+ session.wsClient().permissions().addUser(new org.sonarqube.ws.client.permission.AddUserWsRequest().setLogin(user.getLogin()).setPermission("admin"));
+ session.wsClient().userGroups().addUser(AddUserWsRequest.builder().setLogin(user.getLogin()).setName("sonar-administrators").build());
+ return user;
+ }
+
+ @SafeVarargs
+ public final User generateAdministrator(Organizations.Organization organization, Consumer<CreateRequest.Builder>... populators) {
+ User user = generate(populators);
+ session.wsClient().organizations().addMember(organization.getKey(), user.getLogin());
+ session.wsClient().userGroups().addUser(AddUserWsRequest.builder()
+ .setOrganization(organization.getKey())
+ .setLogin(user.getLogin())
+ .setName("Owners")
+ .build());
+ return user;
+ }
+
+ @SafeVarargs
+ public final User generateMember(Organizations.Organization organization, Consumer<CreateRequest.Builder>... populators) {
+ User user = generate(populators);
+ session.wsClient().organizations().addMember(organization.getKey(), user.getLogin());
+ return user;
+ }
+
+ public UsersService service() {
+ return session.wsClient().users();
+ }
+
+ public Optional<WsUsers.SearchWsResponse.User> getByLogin(String login) {
+ List<WsUsers.SearchWsResponse.User> users = session.wsClient().users().search(SearchRequest.builder().setQuery(login).build()).getUsersList();
+ if (users.size() == 1) {
+ return Optional.of(users.get(0));
+ }
+ return Optional.empty();
+ }
+}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/BackgroundTaskItem.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/BackgroundTaskItem.java
index 0c86d3293b1..d5e09d103a4 100644
--- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/BackgroundTaskItem.java
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/BackgroundTaskItem.java
@@ -48,7 +48,7 @@ public class BackgroundTaskItem {
}
public BackgroundTaskItem assertScannerContextContains(String text) {
- Selenide.$(".js-task-scanner-context").should(Condition.hasText(text));
+ Selenide.$(".js-task-scanner-context").should(Condition.text(text));
return this;
}
@@ -59,7 +59,7 @@ public class BackgroundTaskItem {
}
public BackgroundTaskItem assertErrorStacktraceContains(String text) {
- Selenide.$(".js-task-stacktrace").should(Condition.hasText(text));
+ Selenide.$(".js-task-stacktrace").should(Condition.text(text));
return this;
}
}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/LoginPage.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/LoginPage.java
index b210e01fb2e..8fb508c4ed3 100644
--- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/LoginPage.java
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/LoginPage.java
@@ -54,7 +54,7 @@ public class LoginPage {
return Selenide.$(".process-spinner-failed");
}
- private <T> T submitCredentials(String login, String password, Class<T> expectedResultPage) {
+ private static <T> T submitCredentials(String login, String password, Class<T> expectedResultPage) {
Selenide.$("#login").val(login);
Selenide.$("#password").val(password);
Selenide.$(By.name("commit")).click();
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/MarketplacePage.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/MarketplacePage.java
index 908bad3b3e9..dac1b28ca8c 100644
--- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/MarketplacePage.java
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/MarketplacePage.java
@@ -54,7 +54,7 @@ public class MarketplacePage {
return this;
}
- private SelenideElement getPlugin(String name) {
+ private static SelenideElement getPlugin(String name) {
return Selenide.$$(".js-plugin-name").findBy(Condition.text(name)).should(Condition.exist).parent().parent().parent();
}
}
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 67cea197772..fbfb8862fe7 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
@@ -164,7 +164,7 @@ public class Navigation {
}
public SettingsPage openSettings(@Nullable String projectKey) throws UnsupportedEncodingException {
- String url = projectKey != null ? "/project/settings?id=" + URLEncoder.encode(projectKey, "UTF-8") : "/settings";
+ String url = projectKey != null ? ("/project/settings?id=" + URLEncoder.encode(projectKey, "UTF-8")) : "/settings";
return open(url, SettingsPage.class);
}
@@ -249,11 +249,11 @@ public class Navigation {
return Selenide.$("#error");
}
- private SelenideElement logInLink() {
+ private static SelenideElement logInLink() {
return Selenide.$(By.linkText("Log in"));
}
- private SelenideElement loggedInDropdown() {
+ private static SelenideElement loggedInDropdown() {
return Selenide.$(".js-user-authenticated");
}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/NotificationsPage.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/NotificationsPage.java
index bd4911a94c6..06939de713c 100644
--- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/NotificationsPage.java
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/NotificationsPage.java
@@ -24,7 +24,7 @@ import com.codeborne.selenide.Selenide;
public class NotificationsPage extends Navigation {
- private final String EMAIL = "EmailNotificationChannel";
+ private static final String EMAIL = "EmailNotificationChannel";
public NotificationsPage() {
Selenide.$("#account-page").shouldHave(Condition.text("Overall notifications"));
@@ -90,11 +90,11 @@ public class NotificationsPage extends Navigation {
return this;
}
- private String globalCheckboxSelector(String type, String channel) {
+ private static String globalCheckboxSelector(String type, String channel) {
return "#global-notification-" + type + "-" + channel;
}
- private String projectCheckboxSelector(String project, String type, String channel) {
+ private static String projectCheckboxSelector(String project, String type, String channel) {
return "#project-notification-" + project + "-" + type + "-" + channel;
}
@@ -112,7 +112,7 @@ public class NotificationsPage extends Navigation {
return this;
}
- private void toggleCheckbox(String selector) {
+ private static void toggleCheckbox(String selector) {
Selenide.$(selector).click();
}
}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectActivityPage.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectActivityPage.java
index ad8206cd47d..05a7fbc6bfb 100644
--- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectActivityPage.java
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectActivityPage.java
@@ -54,7 +54,7 @@ public class ProjectActivityPage {
Selenide.$("#project-activity")
.find(".project-activity-day[data-day=\"" + day + "\"]")
.find(".project-activity-analysis")
- .should(Condition.hasText(text));
+ .should(Condition.text(text));
return this;
}
}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectCodePage.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectCodePage.java
index 42bb86748a2..dfdd972427a 100644
--- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectCodePage.java
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectCodePage.java
@@ -24,9 +24,6 @@ import com.codeborne.selenide.Selenide;
public class ProjectCodePage {
- public ProjectCodePage() {
- }
-
public ProjectCodePage openFirstComponent() {
Selenide.$$(".code-name-cell a").first().click();
return this;
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectDashboardPage.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectDashboardPage.java
index 4db286489dd..c30abb98901 100644
--- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectDashboardPage.java
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectDashboardPage.java
@@ -49,11 +49,10 @@ public class ProjectDashboardPage {
public SelenideElement getOverviewMeasure(String measure) {
ElementsCollection measures = Selenide.$$(".overview-domain-measure");
- SelenideElement element = measures.find(Condition.text(measure)).shouldBe(Condition.visible);
- return element;
+ return measures.find(Condition.text(measure)).shouldBe(Condition.visible);
}
- private SelenideElement getTagsMeta() {
+ private static SelenideElement getTagsMeta() {
SelenideElement element = Selenide.$(".overview-meta-tags");
element.shouldBe(Condition.visible);
return element;
@@ -61,38 +60,38 @@ public class ProjectDashboardPage {
public ProjectDashboardPage shouldHaveTags(String... tags) {
String tagsList = String.join(", ", Arrays.asList(tags));
- this.getTagsMeta().$(".tags-list > span").should(Condition.hasText(tagsList));
+ getTagsMeta().$(".tags-list > span").should(Condition.text(tagsList));
return this;
}
public ProjectDashboardPage shouldNotBeEditable() {
- SelenideElement tagsElem = this.getTagsMeta();
+ SelenideElement tagsElem = getTagsMeta();
tagsElem.$("button").shouldNot(Condition.exist);
tagsElem.$("div.multi-select").shouldNot(Condition.exist);
return this;
}
public ProjectDashboardPage shouldBeEditable() {
- SelenideElement tagsElem = this.getTagsMeta();
+ SelenideElement tagsElem = getTagsMeta();
tagsElem.$("button").shouldBe(Condition.visible);
return this;
}
public ProjectDashboardPage openTagEditor() {
- SelenideElement tagsElem = this.getTagsMeta();
+ SelenideElement tagsElem = getTagsMeta();
tagsElem.$("button").shouldBe(Condition.visible).click();
tagsElem.$("div.multi-select").shouldBe(Condition.visible);
return this;
}
public SelenideElement getTagAtIdx(Integer idx) {
- SelenideElement tagsElem = this.getTagsMeta();
+ SelenideElement tagsElem = getTagsMeta();
tagsElem.$("div.multi-select").shouldBe(Condition.visible);
return tagsElem.$$("ul.menu a").get(idx);
}
public ProjectDashboardPage sendKeysToTagsInput(CharSequence... charSequences) {
- SelenideElement tagsInput = this.getTagsMeta().find("input");
+ SelenideElement tagsInput = getTagsMeta().find("input");
tagsInput.sendKeys(charSequences);
return this;
}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectKeyPage.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectKeyPage.java
index 5a3e5ec678b..cfc2e8137ab 100644
--- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectKeyPage.java
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/ProjectKeyPage.java
@@ -77,8 +77,8 @@ public class ProjectKeyPage {
public ProjectKeyPage assertBulkChangeSimulationResult(String oldKey, String newKey) {
SelenideElement row = Selenide.$("#bulk-update-results").$("[data-key=\"" + oldKey + "\"]");
- row.$(".js-old-key").should(Condition.hasText(oldKey));
- row.$(".js-new-key").should(Condition.hasText(newKey));
+ row.$(".js-old-key").should(Condition.text(oldKey));
+ row.$(".js-new-key").should(Condition.text(newKey));
return this;
}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/SystemInfoPageItem.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/SystemInfoPageItem.java
index 55d9c84092e..c047a7be28c 100644
--- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/SystemInfoPageItem.java
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/SystemInfoPageItem.java
@@ -26,8 +26,8 @@ public class SystemInfoPageItem {
private final SelenideElement elt;
public SystemInfoPageItem(SelenideElement elt) {
- this.elt = elt;
- }
+ this.elt = elt;
+ }
public SystemInfoPageItem shouldHaveHealth() {
elt.$(".system-info-health-info .status-indicator").should(Condition.exist);
@@ -72,7 +72,7 @@ public class SystemInfoPageItem {
}
public SystemInfoPageItem ensureOpen() {
- if(!isOpen()) {
+ if (!isOpen()) {
elt.click();
elt.$(".boxed-group-inner").should(Condition.exist);
}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/issues/IssuesPage.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/issues/IssuesPage.java
index 92755921f37..8da8149bcf6 100644
--- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/issues/IssuesPage.java
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/issues/IssuesPage.java
@@ -32,14 +32,6 @@ public class IssuesPage {
Selenide.$(".issues").should(Condition.exist);
}
- private ElementsCollection getIssuesElements() {
- return Selenide.$$(".issues .issue");
- }
-
- private ElementsCollection getIssuesPathComponents() {
- return Selenide.$$(".issues-workspace-list-component");
- }
-
public List<Issue> getIssues() {
return getIssuesElements()
.stream()
@@ -48,7 +40,7 @@ public class IssuesPage {
}
public IssuesPage issuesCount(Integer count) {
- this.getIssuesElements().shouldHaveSize(count);
+ getIssuesElements().shouldHaveSize(count);
return this;
}
@@ -58,12 +50,12 @@ public class IssuesPage {
}
public IssuesPage componentsShouldContain(String path) {
- this.getIssuesPathComponents().forEach(element -> element.shouldHave(Condition.text(path)));
+ getIssuesPathComponents().forEach(element -> element.shouldHave(Condition.text(path)));
return this;
}
public IssuesPage componentsShouldNotContain(String path) {
- this.getIssuesPathComponents().forEach(element -> element.shouldNotHave(Condition.text(path)));
+ getIssuesPathComponents().forEach(element -> element.shouldNotHave(Condition.text(path)));
return this;
}
@@ -79,4 +71,12 @@ public class IssuesPage {
Selenide.$("#issues-bulk-change-assignee .Select-input input").pressEscape();
return this;
}
+
+ private static ElementsCollection getIssuesElements() {
+ return Selenide.$$(".issues .issue");
+ }
+
+ private static ElementsCollection getIssuesPathComponents() {
+ return Selenide.$$(".issues-workspace-list-component");
+ }
}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/issues/package-info.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/issues/package-info.java
new file mode 100644
index 00000000000..a035971ae1e
--- /dev/null
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/issues/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonarqube.qa.util.pageobjects.issues;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/measures/MeasuresPage.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/measures/MeasuresPage.java
index de052a403de..f3cd21aac1b 100644
--- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/measures/MeasuresPage.java
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/measures/MeasuresPage.java
@@ -37,14 +37,14 @@ public class MeasuresPage {
}
public MeasuresPage measureHasValue(String measure, Integer value) {
- SelenideElement sidebar = this.getSideBar();
+ SelenideElement sidebar = getSideBar();
sidebar.$("#measure-" + measure + "-name").should(Condition.exist);
sidebar.$("#measure-" + measure + "-value").should(Condition.exist).shouldHave(Condition.text(value.toString()));
return this;
}
public MeasuresPage measureHasLeak(String measure, Integer value) {
- SelenideElement sidebar = this.getSideBar();
+ SelenideElement sidebar = getSideBar();
sidebar.$("#measure-" + measure + "-name").should(Condition.exist);
sidebar.$("#measure-" + measure + "-leak").should(Condition.exist).shouldHave(Condition.text(value.toString()));
return this;
@@ -83,7 +83,7 @@ public class MeasuresPage {
}
public MeasureContent openMeasureContent(String measure) {
- SelenideElement sidebar = this.getSideBar();
+ SelenideElement sidebar = getSideBar();
SelenideElement facetItem = sidebar.$("#measure-" + measure + "-name");
facetItem.click();
MeasureContent content = new MeasureContent(Selenide.$("#component-measures .measure-details-content").should(Condition.exist));
@@ -91,7 +91,7 @@ public class MeasuresPage {
return content;
}
- private SelenideElement getSideBar() {
+ private static SelenideElement getSideBar() {
return Selenide.$("#component-measures .layout-page-side").should(Condition.exist);
}
}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/measures/package-info.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/measures/package-info.java
new file mode 100644
index 00000000000..64041b2e8d1
--- /dev/null
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/measures/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonarqube.qa.util.pageobjects.measures;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/organization/MemberItem.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/organization/MemberItem.java
index fd02dfb1f11..e25ff4a441c 100644
--- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/organization/MemberItem.java
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/organization/MemberItem.java
@@ -85,7 +85,7 @@ public class MemberItem {
return this;
}
- private SelenideElement getModal(String title) {
+ private static SelenideElement getModal(String title) {
Selenide.$(".modal-head").should(Condition.exist).shouldHave(Condition.text(title));
SelenideElement form = Selenide.$(".ReactModalPortal form");
form.should(Condition.exist);
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/organization/MembersPage.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/organization/MembersPage.java
index 21816aea1c1..d2b560f6db4 100644
--- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/organization/MembersPage.java
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/organization/MembersPage.java
@@ -63,7 +63,7 @@ public class MembersPage {
this.canAddMember();
Selenide.$(".page-actions button").click();
- SelenideElement modal = this.getModal("Add user");
+ SelenideElement modal = getModal("Add user");
SelenideElement input = modal.$(".Select-input input");
input.val(login);
modal.$("div.Select-option.is-focused").should(Condition.exist);
@@ -72,7 +72,7 @@ public class MembersPage {
return this;
}
- private SelenideElement getModal(String title) {
+ private static SelenideElement getModal(String title) {
Selenide.$(".modal-head").should(Condition.exist).shouldHave(Condition.text(title));
SelenideElement form = Selenide.$(".ReactModalPortal form");
form.should(Condition.exist);
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/organization/package-info.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/organization/package-info.java
new file mode 100644
index 00000000000..3928f0134f3
--- /dev/null
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/organization/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonarqube.qa.util.pageobjects.organization;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/projects/ProjectsPage.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/projects/ProjectsPage.java
index cc55ce2d619..a006d627084 100644
--- a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/projects/ProjectsPage.java
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/projects/ProjectsPage.java
@@ -110,7 +110,7 @@ public class ProjectsPage {
return this;
}
- private SelenideElement getOpenTopBar() {
+ private static SelenideElement getOpenTopBar() {
return Selenide.$(".projects-topbar-items").should(Condition.exist);
}
}
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/projects/package-info.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/projects/package-info.java
new file mode 100644
index 00000000000..20aa8b7ca96
--- /dev/null
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/projects/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonarqube.qa.util.pageobjects.projects;
+
+import javax.annotation.ParametersAreNonnullByDefault;
diff --git a/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/settings/package-info.java b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/settings/package-info.java
new file mode 100644
index 00000000000..f60e348b614
--- /dev/null
+++ b/server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/settings/package-info.java
@@ -0,0 +1,23 @@
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 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.
+ */
+@ParametersAreNonnullByDefault
+package org.sonarqube.qa.util.pageobjects.settings;
+
+import javax.annotation.ParametersAreNonnullByDefault;