diff options
author | Simon Brandhof <simon.brandhof@sonarsource.com> | 2017-11-09 17:39:52 +0100 |
---|---|---|
committer | Eric Hartmann <hartmann.eric@gmail.Com> | 2017-11-14 13:10:17 +0100 |
commit | a0677e1efa4db51e563f74548c5ca54a24a17efc (patch) | |
tree | 5a24c3543269d9316c160ebbf826f5789dd8fa96 /server/sonar-qa-util/src | |
parent | c0be4a24b1cf4f3f0fe8314e9e7c70d4cdc61b3c (diff) | |
download | sonarqube-a0677e1efa4db51e563f74548c5ca54a24a17efc.tar.gz sonarqube-a0677e1efa4db51e563f74548c5ca54a24a17efc.zip |
Move helpers to sonar-qa-util
Diffstat (limited to 'server/sonar-qa-util/src')
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; |