]> source.dussan.org Git - sonarqube.git/commitdiff
Add category "project" to integration tests
authorSimon Brandhof <simon.brandhof@sonarsource.com>
Mon, 13 Nov 2017 23:01:28 +0000 (00:01 +0100)
committerEric Hartmann <hartmann.eric@gmail.Com>
Tue, 14 Nov 2017 12:10:17 +0000 (13:10 +0100)
48 files changed:
cix.sh
server/sonar-qa-util/src/main/java/org/sonarqube/qa/util/pageobjects/Navigation.java
tests/src/test/java/org/sonarqube/tests/Category1Suite.java
tests/src/test/java/org/sonarqube/tests/Category2Suite.java
tests/src/test/java/org/sonarqube/tests/Category4Suite.java
tests/src/test/java/org/sonarqube/tests/Category6Suite.java
tests/src/test/java/org/sonarqube/tests/branch/BranchTest.java [deleted file]
tests/src/test/java/org/sonarqube/tests/ce/BackgroundTasksTest.java [new file with mode: 0644]
tests/src/test/java/org/sonarqube/tests/component/BranchTest.java [new file with mode: 0644]
tests/src/test/java/org/sonarqube/tests/component/CodePageTest.java [new file with mode: 0644]
tests/src/test/java/org/sonarqube/tests/component/ComponentSuite.java [new file with mode: 0644]
tests/src/test/java/org/sonarqube/tests/component/ComponentsWsTest.java
tests/src/test/java/org/sonarqube/tests/component/ProjectsWsTest.java [deleted file]
tests/src/test/java/org/sonarqube/tests/measure/MeasureSuite.java
tests/src/test/java/org/sonarqube/tests/measure/ProjectActivityPageTest.java [new file with mode: 0644]
tests/src/test/java/org/sonarqube/tests/project/ProjectBulkDeletionPageTest.java [new file with mode: 0644]
tests/src/test/java/org/sonarqube/tests/project/ProjectBulkDeletionTest.java [new file with mode: 0644]
tests/src/test/java/org/sonarqube/tests/project/ProjectDeletionTest.java [new file with mode: 0644]
tests/src/test/java/org/sonarqube/tests/project/ProjectFilterTest.java [new file with mode: 0644]
tests/src/test/java/org/sonarqube/tests/project/ProjectKeyUpdateTest.java [new file with mode: 0644]
tests/src/test/java/org/sonarqube/tests/project/ProjectLeakPageTest.java [new file with mode: 0644]
tests/src/test/java/org/sonarqube/tests/project/ProjectLinksTest.java [new file with mode: 0644]
tests/src/test/java/org/sonarqube/tests/project/ProjectListTest.java [new file with mode: 0644]
tests/src/test/java/org/sonarqube/tests/project/ProjectProvisioningTest.java [new file with mode: 0644]
tests/src/test/java/org/sonarqube/tests/project/ProjectSearchTest.java [new file with mode: 0644]
tests/src/test/java/org/sonarqube/tests/project/ProjectSuite.java [new file with mode: 0644]
tests/src/test/java/org/sonarqube/tests/project/ProjectVisibilityPageTest.java [new file with mode: 0644]
tests/src/test/java/org/sonarqube/tests/project/ProjectsPageTest.java [new file with mode: 0644]
tests/src/test/java/org/sonarqube/tests/projectAdministration/BackgroundTasksTest.java [deleted file]
tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectAdministrationTest.java
tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectBulkDeleteTest.java [deleted file]
tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectBulkDeletionPageTest.java [deleted file]
tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectDeleteTest.java [deleted file]
tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectKeyUpdateTest.java [deleted file]
tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectLinksPageTest.java [deleted file]
tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectProvisioningTest.java [deleted file]
tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectQualityGatePageTest.java [deleted file]
tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectSearchTest.java [deleted file]
tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectVisibilityPageTest.java [deleted file]
tests/src/test/java/org/sonarqube/tests/projectEvent/ProjectActivityPageTest.java [deleted file]
tests/src/test/java/org/sonarqube/tests/projectSearch/LeakProjectsPageTest.java [deleted file]
tests/src/test/java/org/sonarqube/tests/projectSearch/ProjectsPageTest.java [deleted file]
tests/src/test/java/org/sonarqube/tests/projectSearch/SearchProjectsTest.java [deleted file]
tests/src/test/java/org/sonarqube/tests/qualityGate/ProjectQualityGatePageTest.java [new file with mode: 0644]
tests/src/test/java/org/sonarqube/tests/qualityGate/QualityGateSuite.java
tests/src/test/java/org/sonarqube/tests/source/ProjectCodeTest.java [deleted file]
tests/src/test/java/org/sonarqube/tests/source/SourceSuite.java
tests/src/test/java/org/sonarqube/tests/ui/UiTest.java

diff --git a/cix.sh b/cix.sh
index 073f4b76a525f131f0df4ca26dbdd25d88a776a2..6acdae1ff4e624f33a4dd82bb55c29c102a74700 100755 (executable)
--- a/cix.sh
+++ b/cix.sh
@@ -30,7 +30,7 @@ case "$RUN_ACTIVITY" in
           ;;
 
         Category3)
-          CATEGORY="Category3"
+          CATEGORY="Category3|component|project"
           ;;
 
         Category4)
index b9770335aad766ee3d2c5a8e124d9107aa307c3b..25fc6937c8eae8dea51182ffab9a0eca7cc670c8 100644 (file)
@@ -187,6 +187,13 @@ public class Navigation {
     return open("/projects_admin", ProjectsManagementPage.class);
   }
 
+  /**
+   * Should be replaced by an intermediary OrganizationPage
+   */
+  public ProjectsManagementPage openProjectsManagement(String orgKey) {
+    return open("/organizations/" + orgKey + "/projects_management", ProjectsManagementPage.class);
+  }
+
   public LoginPage openLogin() {
     return open("/sessions/login", LoginPage.class);
   }
index 2393e6818795758217fa5ac4a83cbcc574278eaf..34afd54d11bddd31c7080a5c4163944a734dcb29 100644 (file)
@@ -23,12 +23,8 @@ import com.sonar.orchestrator.Orchestrator;
 import org.junit.ClassRule;
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
-import org.sonarqube.tests.projectAdministration.BackgroundTasksTest;
+import org.sonarqube.tests.ce.BackgroundTasksTest;
 import org.sonarqube.tests.projectAdministration.ProjectAdministrationTest;
-import org.sonarqube.tests.projectAdministration.ProjectBulkDeletionPageTest;
-import org.sonarqube.tests.projectAdministration.ProjectLinksPageTest;
-import org.sonarqube.tests.projectAdministration.ProjectVisibilityPageTest;
-import org.sonarqube.tests.projectSearch.ProjectsPageTest;
 import org.sonarqube.tests.settings.DeprecatedPropertiesWsTest;
 import org.sonarqube.tests.settings.EmailsTest;
 import org.sonarqube.tests.settings.PropertySetsTest;
@@ -45,21 +41,13 @@ import static util.ItUtils.xooPlugin;
 @Deprecated
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
-  // administration
   UsersPageTest.class,
-  ProjectVisibilityPageTest.class,
-  // project administration
-  ProjectBulkDeletionPageTest.class,
   ProjectAdministrationTest.class,
-  ProjectLinksPageTest.class,
   BackgroundTasksTest.class,
-  // settings
   DeprecatedPropertiesWsTest.class,
   EmailsTest.class,
   PropertySetsTest.class,
-  SettingsTest.class,
-  // measure
-  ProjectsPageTest.class
+  SettingsTest.class
 })
 public class Category1Suite {
 
index 03fd21935d780ee43009992a1b16e665f31192f6..619aa95106b402b054e2192c5bf7f45cab1e6acc 100644 (file)
@@ -23,7 +23,7 @@ import com.sonar.orchestrator.Orchestrator;
 import org.junit.ClassRule;
 import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
-import org.sonarqube.tests.branch.BranchTest;
+import org.sonarqube.tests.component.BranchTest;
 import org.sonarqube.tests.issue.AutoAssignTest;
 import org.sonarqube.tests.issue.CommonRulesTest;
 import org.sonarqube.tests.issue.CustomRulesTest;
index 6c2d14ae4ba5a8e8e9e70e3a7765f0a53c0b9dcc..94d9a43693aefa0d2bf49c291d9f23a4308978a8 100644 (file)
@@ -27,9 +27,6 @@ import org.sonarqube.tests.analysis.FileExclusionsTest;
 import org.sonarqube.tests.analysis.IssueExclusionsTest;
 import org.sonarqube.tests.ce.CeTempDirTest;
 import org.sonarqube.tests.ce.CeWsTest;
-import org.sonarqube.tests.component.ComponentsWsTest;
-import org.sonarqube.tests.component.ProjectsWsTest;
-import org.sonarqube.tests.projectEvent.ProjectActivityPageTest;
 import org.sonarqube.tests.qualityProfile.QualityProfilesUiTest;
 import org.sonarqube.tests.serverSystem.HttpHeadersTest;
 import org.sonarqube.tests.serverSystem.LogsTest;
@@ -72,14 +69,9 @@ import static util.ItUtils.xooPlugin;
   LocalAuthenticationTest.class,
   BaseIdentityProviderTest.class,
   OAuth2IdentityProviderTest.class,
-  // component search
-  ProjectsWsTest.class,
-  ComponentsWsTest.class,
   // analysis exclusion
   FileExclusionsTest.class,
   IssueExclusionsTest.class,
-  // db cleaner
-  ProjectActivityPageTest.class,
   // http
   HttpHeadersTest.class,
   // ui
index 87ebf3ac247e214ead6e4488d77df7bc48adeb9a..9cb93ba6474586a0af2a1c7a8e8a1cfe9a589fc0 100644 (file)
@@ -31,12 +31,6 @@ import org.sonarqube.tests.issue.IssueNotificationsTest;
 import org.sonarqube.tests.issue.IssueTagsTest;
 import org.sonarqube.tests.issue.OrganizationIssueAssignTest;
 import org.sonarqube.tests.issue.OrganizationIssuesPageTest;
-import org.sonarqube.tests.projectAdministration.ProjectDeleteTest;
-import org.sonarqube.tests.projectAdministration.ProjectKeyUpdateTest;
-import org.sonarqube.tests.projectAdministration.ProjectProvisioningTest;
-import org.sonarqube.tests.projectAdministration.ProjectSearchTest;
-import org.sonarqube.tests.projectSearch.LeakProjectsPageTest;
-import org.sonarqube.tests.projectSearch.SearchProjectsTest;
 import org.sonarqube.tests.qualityProfile.BuiltInQualityProfilesTest;
 import org.sonarqube.tests.qualityProfile.CustomQualityProfilesTest;
 import org.sonarqube.tests.qualityProfile.QualityProfilesEditTest;
@@ -64,13 +58,7 @@ import static util.ItUtils.xooPlugin;
   QualityProfilesWsTest.class,
   CustomQualityProfilesTest.class,
   IssueTagsTest.class,
-  LeakProjectsPageTest.class,
-  SearchProjectsTest.class,
   RulesWsTest.class,
-  ProjectDeleteTest.class,
-  ProjectProvisioningTest.class,
-  ProjectKeyUpdateTest.class,
-  ProjectSearchTest.class,
   PermissionTemplateTest.class,
   ReportFailureNotificationTest.class,
   IssueNotificationsTest.class
diff --git a/tests/src/test/java/org/sonarqube/tests/branch/BranchTest.java b/tests/src/test/java/org/sonarqube/tests/branch/BranchTest.java
deleted file mode 100644 (file)
index aef3ab0..0000000
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * 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.tests.branch;
-
-import com.sonar.orchestrator.Orchestrator;
-import java.util.Map;
-import org.assertj.core.groups.Tuple;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonarqube.tests.Category2Suite;
-import org.sonarqube.qa.util.Tester;
-import org.sonarqube.ws.Common;
-import org.sonarqube.ws.WsBranches;
-import org.sonarqube.ws.client.GetRequest;
-import org.sonarqube.ws.client.WsResponse;
-import util.ItUtils;
-
-import static org.assertj.core.api.Java6Assertions.assertThat;
-import static util.ItUtils.runProjectAnalysis;
-
-public class BranchTest {
-
-  @ClassRule
-  public static Orchestrator orchestrator = Category2Suite.ORCHESTRATOR;
-
-  @Rule
-  public Tester tester = new Tester(orchestrator).disableOrganizations();
-
-  @Test
-  public void list_branches_contains_main_branch() {
-    runProjectAnalysis(orchestrator, "shared/xoo-sample");
-
-    WsBranches.ListWsResponse result = tester.wsClient().projectBranches().list("sample");
-
-    assertThat(result.getBranchesList())
-      .extracting(WsBranches.Branch::getName, WsBranches.Branch::getType, WsBranches.Branch::getIsMain)
-      .containsExactlyInAnyOrder(Tuple.tuple("master", Common.BranchType.LONG, true));
-  }
-
-  @Test
-  public void navigation_global_return_branches_support_to_false() {
-    WsResponse status = tester.wsClient().wsConnector().call(new GetRequest("api/navigation/global"));
-    Map<String, Object> statusMap = ItUtils.jsonToMap(status.content());
-
-    assertThat(statusMap.get("branchesEnabled")).isEqualTo(false);
-  }
-}
diff --git a/tests/src/test/java/org/sonarqube/tests/ce/BackgroundTasksTest.java b/tests/src/test/java/org/sonarqube/tests/ce/BackgroundTasksTest.java
new file mode 100644 (file)
index 0000000..059bfa2
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * 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.tests.ce;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import org.sonarqube.tests.Category1Suite;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonarqube.qa.util.pageobjects.BackgroundTaskItem;
+import org.sonarqube.qa.util.pageobjects.BackgroundTasksPage;
+import org.sonarqube.qa.util.pageobjects.Navigation;
+import util.user.UserRule;
+
+import static com.codeborne.selenide.CollectionCondition.sizeGreaterThan;
+import static util.ItUtils.projectDir;
+import static util.selenium.Selenese.runSelenese;
+
+public class BackgroundTasksTest {
+
+  private static final String ADMIN_USER_LOGIN = "admin-user";
+
+  @ClassRule
+  public static Orchestrator ORCHESTRATOR = Category1Suite.ORCHESTRATOR;
+
+  @Rule
+  public UserRule userRule = UserRule.from(ORCHESTRATOR);
+
+  private Navigation nav = Navigation.create(ORCHESTRATOR);
+
+  @BeforeClass
+  public static void beforeClass() {
+    executeBuild("test-project", "Test Project");
+    executeBuild("test-project-2", "Another Test Project");
+  }
+
+  @Before
+  public void before() {
+    userRule.createAdminUser(ADMIN_USER_LOGIN, ADMIN_USER_LOGIN);
+  }
+
+  @After
+  public void deleteAdminUser() {
+    userRule.resetUsers();
+  }
+
+  @Test
+  public void should_not_display_failing_and_search_and_filter_elements_on_project_level_page() throws Exception {
+    runSelenese(ORCHESTRATOR, "/projectAdministration/BackgroundTasksTest/should_not_display_failing_and_search_and_filter_elements_on_project_level_page.html");
+  }
+
+  @Test
+  public void display_scanner_context() {
+    nav.logIn().submitCredentials(ADMIN_USER_LOGIN);
+    BackgroundTasksPage page = nav.openBackgroundTasksPage();
+
+    page.getTasks().shouldHave(sizeGreaterThan(0));
+    BackgroundTaskItem task = page.getTasksAsItems().get(0);
+    task.openActions()
+      .openScannerContext()
+      .assertScannerContextContains("SonarQube plugins:")
+      .assertScannerContextContains("Global properties:");
+  }
+
+  @Test
+  public void display_error_stacktrace() {
+    Navigation nav = Navigation.create(ORCHESTRATOR);
+    executeBuild("test-project", "Test Project", "2010-01-01");
+
+    nav.logIn().submitCredentials(ADMIN_USER_LOGIN);
+    BackgroundTasksPage page = nav.openBackgroundTasksPage();
+
+    page.getTasks().shouldHave(sizeGreaterThan(0));
+    BackgroundTaskItem task = page.getTasksAsItems().get(0);
+    task.openActions()
+      .openErrorStacktrace()
+      .assertErrorStacktraceContains("Date of analysis cannot be older than the date of the last known analysis");
+  }
+
+  private static void executeBuild(String projectKey, String projectName) {
+    ORCHESTRATOR.executeBuild(
+      SonarScanner.create(projectDir("shared/xoo-sample"))
+        .setProjectKey(projectKey)
+        .setProjectName(projectName));
+  }
+
+  private static void executeBuild(String projectKey, String projectName, String date) {
+    ORCHESTRATOR.executeBuild(
+      SonarScanner.create(projectDir("shared/xoo-sample"))
+        .setProjectKey(projectKey)
+        .setProjectName(projectName)
+        .setProperty("sonar.projectDate", date));
+  }
+}
diff --git a/tests/src/test/java/org/sonarqube/tests/component/BranchTest.java b/tests/src/test/java/org/sonarqube/tests/component/BranchTest.java
new file mode 100644 (file)
index 0000000..feb667d
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * 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.tests.component;
+
+import com.sonar.orchestrator.Orchestrator;
+import java.util.Map;
+import org.assertj.core.groups.Tuple;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonarqube.qa.util.Tester;
+import org.sonarqube.ws.Common;
+import org.sonarqube.ws.WsBranches;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.WsResponse;
+import util.ItUtils;
+
+import static org.assertj.core.api.Java6Assertions.assertThat;
+import static util.ItUtils.runProjectAnalysis;
+
+public class BranchTest {
+
+  @ClassRule
+  public static Orchestrator orchestrator = ComponentSuite.ORCHESTRATOR;
+
+  @Rule
+  public Tester tester = new Tester(orchestrator);
+
+  @Test
+  public void list_branches_contains_main_branch() {
+    runProjectAnalysis(orchestrator, "shared/xoo-sample");
+
+    WsBranches.ListWsResponse result = tester.wsClient().projectBranches().list("sample");
+
+    assertThat(result.getBranchesList())
+      .extracting(WsBranches.Branch::getName, WsBranches.Branch::getType, WsBranches.Branch::getIsMain)
+      .containsExactlyInAnyOrder(Tuple.tuple("master", Common.BranchType.LONG, true));
+  }
+
+  @Test
+  public void navigation_global_return_branches_support_to_false() {
+    WsResponse status = tester.wsClient().wsConnector().call(new GetRequest("api/navigation/global"));
+    Map<String, Object> statusMap = ItUtils.jsonToMap(status.content());
+
+    assertThat(statusMap.get("branchesEnabled")).isEqualTo(false);
+  }
+}
diff --git a/tests/src/test/java/org/sonarqube/tests/component/CodePageTest.java b/tests/src/test/java/org/sonarqube/tests/component/CodePageTest.java
new file mode 100644 (file)
index 0000000..1f1b33f
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * 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.tests.component;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonarqube.qa.util.Tester;
+import org.sonarqube.ws.WsProjects.CreateWsResponse.Project;
+
+import static util.ItUtils.projectDir;
+
+public class CodePageTest {
+
+  @ClassRule
+  public static Orchestrator orchestrator = ComponentSuite.ORCHESTRATOR;
+
+  @Rule
+  public Tester tester = new Tester(orchestrator);
+
+  @Test
+  public void code_page() {
+    Project project = tester.projects().generate(null);
+    executeAnalysis(project);
+
+    tester.openBrowser().openCode(project.getKey())
+      .shouldHaveComponent("src/main/xoo/sample")
+      .openFirstComponent()
+      .shouldHaveComponent("Sample.xoo")
+      .openFirstComponent()
+      .shouldHaveCode("public class Sample")
+      .shouldHaveBreadcrumbs(project.getName(), "src/main/xoo/sample", "Sample.xoo");
+
+    // search
+    tester.openBrowser().openCode(project.getKey())
+      .shouldHaveComponent(project.getName())
+      .search("xoo")
+      .shouldSearchResult("Sample.xoo");
+
+    // permalink
+    tester.openBrowser().openCode(project.getKey(), project.getKey() + ":src/main/xoo/sample/Sample.xoo")
+      .shouldHaveCode("public class Sample")
+      .shouldHaveBreadcrumbs(project.getName(), "src/main/xoo/sample", "Sample.xoo");
+  }
+
+  @Test
+  public void expand_root_dir() {
+    Project project = tester.projects().generate(null);
+    executeAnalysis(project, "shared/xoo-sample-with-root-dir");
+
+    tester.openBrowser().openCode(project.getKey())
+      .shouldHaveComponent("Hello.xoo")
+      .shouldHaveComponent("src/main/xoo/sample");
+  }
+
+  private void executeAnalysis(Project project, String path) {
+    orchestrator.executeBuild(
+      SonarScanner.create(projectDir(path))
+        .setProjectKey(project.getKey())
+        .setProjectName(project.getName()));
+  }
+
+  private void executeAnalysis(Project project) {
+    executeAnalysis(project, "shared/xoo-sample");
+  }
+}
diff --git a/tests/src/test/java/org/sonarqube/tests/component/ComponentSuite.java b/tests/src/test/java/org/sonarqube/tests/component/ComponentSuite.java
new file mode 100644 (file)
index 0000000..3662490
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * 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.tests.component;
+
+import com.sonar.orchestrator.Orchestrator;
+import org.junit.ClassRule;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+import static util.ItUtils.xooPlugin;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+  BranchTest.class,
+  CodePageTest.class,
+  ComponentsWsTest.class
+})
+public class ComponentSuite {
+
+  @ClassRule
+  public static final Orchestrator ORCHESTRATOR = Orchestrator.builderEnv()
+    // reduce memory for Elasticsearch
+    .setServerProperty("sonar.search.javaOpts", "-Xms128m -Xmx128m")
+
+    .addPlugin(xooPlugin())
+
+    .build();
+
+}
index 69cae2a8d34dbcdda1d19b65225326ecae1b536b..2e9ccdd7819efc230550d6e03e8ec7939cdc0108 100644 (file)
@@ -21,43 +21,39 @@ package org.sonarqube.tests.component;
 
 import com.sonar.orchestrator.Orchestrator;
 import com.sonar.orchestrator.build.SonarScanner;
-import org.sonarqube.tests.Category4Suite;
-import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.ClassRule;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.ExpectedException;
+import org.junit.rules.RuleChain;
+import org.sonarqube.qa.util.Tester;
 import org.sonarqube.ws.WsComponents;
-import org.sonarqube.ws.client.WsClient;
 import org.sonarqube.ws.client.component.SearchWsRequest;
 import org.sonarqube.ws.client.component.ShowWsRequest;
-import util.ItUtils;
 
 import static java.util.Collections.singletonList;
 import static org.assertj.core.api.Assertions.assertThat;
 import static util.ItUtils.projectDir;
 
 public class ComponentsWsTest {
-  @ClassRule
-  public static final Orchestrator orchestrator = Category4Suite.ORCHESTRATOR;
+
   private static final String FILE_KEY = "sample:src/main/xoo/sample/Sample.xoo";
 
-  @Rule
-  public ExpectedException expectedException = ExpectedException.none();
+  @ClassRule
+  public static final Orchestrator orchestrator = ComponentSuite.ORCHESTRATOR;
+
+  private static Tester tester = new Tester(orchestrator);
 
-  WsClient wsClient;
+  @ClassRule
+  public static RuleChain ruleChain = RuleChain.outerRule(orchestrator).around(tester);
 
-  @Before
-  public void inspectProject() {
-    orchestrator.resetData();
+  @BeforeClass
+  public static void setUp() {
     orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-sample")));
-
-    wsClient = ItUtils.newAdminWsClient(orchestrator);
   }
 
   @Test
   public void show() {
-    WsComponents.ShowWsResponse response = wsClient.components().show(new ShowWsRequest().setKey(FILE_KEY));
+    WsComponents.ShowWsResponse response = tester.wsClient().components().show(new ShowWsRequest().setKey(FILE_KEY));
 
     assertThat(response).isNotNull();
     assertThat(response.getComponent().getKey()).isEqualTo(FILE_KEY);
@@ -66,7 +62,7 @@ public class ComponentsWsTest {
 
   @Test
   public void search() {
-    WsComponents.SearchWsResponse response = wsClient.components().search(new SearchWsRequest()
+    WsComponents.SearchWsResponse response = tester.wsClient().components().search(new SearchWsRequest()
       .setQualifiers(singletonList("FIL")));
 
     assertThat(response).isNotNull();
diff --git a/tests/src/test/java/org/sonarqube/tests/component/ProjectsWsTest.java b/tests/src/test/java/org/sonarqube/tests/component/ProjectsWsTest.java
deleted file mode 100644 (file)
index 75d5f5b..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * 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.tests.component;
-
-import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.build.SonarScanner;
-import org.sonarqube.tests.Category4Suite;
-import java.io.IOException;
-import org.apache.commons.io.IOUtils;
-import org.apache.http.HttpResponse;
-import org.apache.http.client.HttpClient;
-import org.apache.http.client.methods.HttpGet;
-import org.apache.http.impl.client.DefaultHttpClient;
-import org.apache.http.util.EntityUtils;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.sonarqube.ws.WsComponents;
-import org.sonarqube.ws.WsProjects.BulkUpdateKeyWsResponse;
-import org.sonarqube.ws.WsProjects.BulkUpdateKeyWsResponse.Key;
-import org.sonarqube.ws.client.WsClient;
-import org.sonarqube.ws.client.component.ShowWsRequest;
-import org.sonarqube.ws.client.project.BulkUpdateKeyWsRequest;
-import org.sonarqube.ws.client.project.UpdateKeyWsRequest;
-import util.ItUtils;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static util.ItUtils.projectDir;
-
-public class ProjectsWsTest {
-
-  @ClassRule
-  public static final Orchestrator orchestrator = Category4Suite.ORCHESTRATOR;
-  private static final String PROJECT_KEY = "sample";
-
-  @Rule
-  public ExpectedException expectedException = ExpectedException.none();
-
-  private WsClient wsClient;
-
-  @Before
-  public void inspectProject() {
-    orchestrator.resetData();
-    orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-sample")));
-
-    wsClient = ItUtils.newAdminWsClient(orchestrator);
-  }
-
-  /**
-   * SONAR-3105
-   */
-  @Test
-  public void projects_web_service() throws IOException {
-    SonarScanner build = SonarScanner.create(projectDir("shared/xoo-sample"));
-    orchestrator.executeBuild(build);
-
-    String url = orchestrator.getServer().getUrl() + "/api/projects/index?key=sample&versions=true";
-    HttpClient httpclient = new DefaultHttpClient();
-    try {
-      HttpGet get = new HttpGet(url);
-      HttpResponse response = httpclient.execute(get);
-
-      assertThat(response.getStatusLine().getStatusCode()).isEqualTo(200);
-      String content = IOUtils.toString(response.getEntity().getContent());
-      assertThat(content).doesNotContain("error");
-      assertThat(content).contains("sample");
-      EntityUtils.consume(response.getEntity());
-
-    } finally {
-      httpclient.getConnectionManager().shutdown();
-    }
-  }
-
-  @Test
-  public void update_key() {
-    String newProjectKey = "another_project_key";
-    WsComponents.Component project = wsClient.components().show(new ShowWsRequest().setKey(PROJECT_KEY)).getComponent();
-    assertThat(project.getKey()).isEqualTo(PROJECT_KEY);
-
-    wsClient.projects().updateKey(UpdateKeyWsRequest.builder()
-      .setKey(PROJECT_KEY)
-      .setNewKey(newProjectKey)
-      .build());
-
-    assertThat(wsClient.components().show(new ShowWsRequest().setId(project.getId())).getComponent().getKey()).isEqualTo(newProjectKey);
-  }
-
-  @Test
-  public void bulk_update_key() {
-    String newProjectKey = "another_project_key";
-    WsComponents.Component project = wsClient.components().show(new ShowWsRequest().setKey(PROJECT_KEY)).getComponent();
-    assertThat(project.getKey()).isEqualTo(PROJECT_KEY);
-
-    BulkUpdateKeyWsResponse result = wsClient.projects().bulkUpdateKey(BulkUpdateKeyWsRequest.builder()
-      .setKey(PROJECT_KEY)
-      .setFrom(PROJECT_KEY)
-      .setTo(newProjectKey)
-      .build());
-
-    assertThat(wsClient.components().show(new ShowWsRequest().setId(project.getId())).getComponent().getKey()).isEqualTo(newProjectKey);
-    assertThat(result.getKeysCount()).isEqualTo(1);
-    assertThat(result.getKeys(0))
-      .extracting(Key::getKey, Key::getNewKey, Key::getDuplicate)
-      .containsOnlyOnce(PROJECT_KEY, newProjectKey, false);
-  }
-}
index ffc9325163fb944774f15e41318ba3e562fa904d..25f8b8ac230240062a15f1a63e1672c2a540059f 100644 (file)
@@ -34,6 +34,7 @@ import static util.ItUtils.xooPlugin;
   DecimalScaleMetricTest.class,
   DifferentialPeriodsTest.class,
   MeasuresWsTest.class,
+  ProjectActivityPageTest.class,
   ProjectDashboardTest.class,
   ProjectMeasuresPageTest.class,
   SincePreviousVersionHistoryTest.class,
diff --git a/tests/src/test/java/org/sonarqube/tests/measure/ProjectActivityPageTest.java b/tests/src/test/java/org/sonarqube/tests/measure/ProjectActivityPageTest.java
new file mode 100644 (file)
index 0000000..188c728
--- /dev/null
@@ -0,0 +1,93 @@
+/*
+ * 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.tests.measure;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import java.util.List;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonarqube.qa.util.Tester;
+import org.sonarqube.qa.util.pageobjects.ProjectActivityPage;
+import org.sonarqube.qa.util.pageobjects.ProjectAnalysisItem;
+
+import static util.ItUtils.projectDir;
+
+public class ProjectActivityPageTest {
+
+  @ClassRule
+  public static Orchestrator orchestrator = MeasureSuite.ORCHESTRATOR;
+
+  @Rule
+  public Tester tester = new Tester(orchestrator);
+
+  @Test
+  public void should_list_snapshots() {
+    analyzeXooSample("shared/xoo-history-v1", "2014-10-19");
+    analyzeXooSample("shared/xoo-history-v2", "2014-11-13");
+
+    ProjectActivityPage page = openPage();
+    page.getAnalyses().shouldHaveSize(2);
+
+    List<ProjectAnalysisItem> analyses = page.getAnalysesAsItems();
+    analyses.get(0)
+      .shouldHaveEventWithText("1.0-SNAPSHOT")
+      .shouldNotHaveDeleteButton();
+
+    analyses.get(1)
+      .shouldHaveEventWithText("0.9-SNAPSHOT")
+      .shouldHaveDeleteButton();
+  }
+
+  @Test
+  public void add_change_delete_custom_event() {
+    analyzeXooSample();
+    openPage().getLastAnalysis()
+      .addCustomEvent("foo")
+      .changeFirstEvent("bar")
+      .deleteFirstEvent();
+  }
+
+  @Test
+  public void delete_analysis() {
+    analyzeXooSample();
+    analyzeXooSample();
+    ProjectActivityPage page = openPage();
+    page.getAnalyses().shouldHaveSize(2);
+    page.getFirstAnalysis().delete();
+  }
+
+  private ProjectActivityPage openPage() {
+    String userAdmin = tester.users().generateAdministratorOnDefaultOrganization().getLogin();
+    return tester.openBrowser()
+      .logIn()
+      .submitCredentials(userAdmin, userAdmin)
+      .openProjectActivity("sample");
+  }
+
+  private void analyzeXooSample() {
+    orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-sample")));
+  }
+
+  private  void analyzeXooSample(String path, String date) {
+    orchestrator.executeBuild(SonarScanner.create(projectDir(path)).setProperties("sonar.projectDate", date));
+  }
+}
diff --git a/tests/src/test/java/org/sonarqube/tests/project/ProjectBulkDeletionPageTest.java b/tests/src/test/java/org/sonarqube/tests/project/ProjectBulkDeletionPageTest.java
new file mode 100644 (file)
index 0000000..a040b04
--- /dev/null
@@ -0,0 +1,74 @@
+/*
+ * 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.tests.project;
+
+import com.sonar.orchestrator.Orchestrator;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonarqube.qa.util.Tester;
+import org.sonarqube.ws.WsProjects.CreateWsResponse.Project;
+import org.sonarqube.ws.client.component.SearchProjectsRequest;
+
+import static com.codeborne.selenide.Condition.text;
+import static com.codeborne.selenide.Condition.visible;
+import static com.codeborne.selenide.Selenide.$;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ProjectBulkDeletionPageTest {
+
+  @ClassRule
+  public static Orchestrator orchestrator = ProjectSuite.ORCHESTRATOR;
+
+  @Rule
+  public Tester tester = new Tester(orchestrator);
+
+  private String sysAdminLogin;
+
+  @Before
+  public void setUp() {
+    sysAdminLogin = tester.users().generateAdministratorOnDefaultOrganization().getLogin();
+  }
+
+  /**
+   * SONAR-2614, SONAR-3805
+   */
+  @Test
+  public void bulk_deletion_on_selected_projects() throws Exception {
+    Project project1 = tester.projects().generate(null, t -> t.setName("Foo"));
+    Project project2 = tester.projects().generate(null, t -> t.setName("Bar"));
+    Project project3 = tester.projects().generate(null, t -> t.setName("FooQux"));
+
+    tester.openBrowser().logIn().submitCredentials(sysAdminLogin).open("/organizations/default-organization/projects_management");
+    $("#projects-management-page").shouldHave(text(project1.getName())).shouldHave(text(project2.getName())).shouldHave(text(project3.getName()));
+
+    $("#projects-management-page .search-box-input").val("foo").pressEnter();
+    $("#projects-management-page").shouldNotHave(text(project2.getName())).shouldHave(text(project1.getName())).shouldHave(text(project3.getName()));
+
+    $("#projects-management-page .js-delete").click();
+    $(".modal").shouldBe(visible);
+    $(".modal button").click();
+    $("#projects-management-page").shouldNotHave(text(project1.getName())).shouldNotHave(text(project3.getName()));
+
+    assertThat(tester.wsClient().components().searchProjects(SearchProjectsRequest.builder().build())
+      .getComponentsCount()).isEqualTo(1);
+  }
+}
diff --git a/tests/src/test/java/org/sonarqube/tests/project/ProjectBulkDeletionTest.java b/tests/src/test/java/org/sonarqube/tests/project/ProjectBulkDeletionTest.java
new file mode 100644 (file)
index 0000000..559589f
--- /dev/null
@@ -0,0 +1,86 @@
+/*
+ * 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.tests.project;
+
+import com.sonar.orchestrator.Orchestrator;
+import java.util.List;
+import java.util.stream.IntStream;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonarqube.qa.util.Tester;
+import org.sonarqube.ws.Organizations;
+import org.sonarqube.ws.WsProjects.CreateWsResponse;
+import org.sonarqube.ws.WsProjects.SearchWsResponse.Component;
+import org.sonarqube.ws.client.project.SearchWsRequest;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static util.ItUtils.runProjectAnalysis;
+
+public class ProjectBulkDeletionTest {
+
+  @ClassRule
+  public static Orchestrator orchestrator = ProjectSuite.ORCHESTRATOR;
+
+  @Rule
+  public Tester tester = new Tester(orchestrator);
+
+  @Test
+  public void delete_projects() {
+    Organizations.Organization organization = tester.organizations().generate();
+    CreateWsResponse.Project firstProvisionedProject = tester.projects().generate(organization, p -> p.setKey("first-provisioned-project"));
+    CreateWsResponse.Project secondProvisionedProject = tester.projects().generate(organization, p -> p.setKey("second-provisioned-project"));
+    CreateWsResponse.Project analyzedProject = tester.projects().generate(organization);
+
+    analyzeProject(analyzedProject.getKey(), organization.getKey());
+
+    tester.wsClient().projects().bulkDelete(SearchWsRequest.builder()
+      .setOrganization(organization.getKey())
+      .setQuery("FIRST-PROVISIONED")
+      .setOnProvisionedOnly(true).build());
+
+    List<Component> projects = tester.wsClient().projects().search(SearchWsRequest.builder().setOrganization(organization.getKey()).build()).getComponentsList();
+    assertThat(projects).extracting(Component::getKey)
+      .containsExactlyInAnyOrder(analyzedProject.getKey(), secondProvisionedProject.getKey())
+      .doesNotContain(firstProvisionedProject.getKey());
+  }
+
+  @Test
+  public void delete_more_than_50_projects_at_the_same_time() {
+    Organizations.Organization organization = tester.organizations().generate();
+    IntStream.range(0, 60).forEach(i -> tester.projects().generate(organization));
+    SearchWsRequest request = SearchWsRequest.builder().setOrganization(organization.getKey()).build();
+    assertThat(tester.wsClient().projects().search(request).getPaging().getTotal()).isEqualTo(60);
+
+    tester.wsClient().projects().bulkDelete(request);
+
+    assertThat(tester.wsClient().projects().search(request).getComponentsList()).isEmpty();
+    assertThat(tester.wsClient().projects().search(request).getPaging().getTotal()).isEqualTo(0);
+  }
+
+  private void analyzeProject(String projectKey, String organizationKey) {
+    runProjectAnalysis(orchestrator, "shared/xoo-sample",
+      "sonar.organization", organizationKey,
+      "sonar.projectKey", projectKey,
+      "sonar.login", "admin",
+      "sonar.password", "admin");
+  }
+}
diff --git a/tests/src/test/java/org/sonarqube/tests/project/ProjectDeletionTest.java b/tests/src/test/java/org/sonarqube/tests/project/ProjectDeletionTest.java
new file mode 100644 (file)
index 0000000..abd50a8
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * 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.tests.project;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+import java.util.stream.Collectors;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.DisableOnDebug;
+import org.junit.rules.TestRule;
+import org.junit.rules.Timeout;
+import org.sonarqube.qa.util.Tester;
+import org.sonarqube.ws.Organizations;
+import org.sonarqube.ws.WsComponents;
+import org.sonarqube.ws.WsProjects;
+import org.sonarqube.ws.WsProjects.CreateWsResponse.Project;
+import org.sonarqube.ws.WsUsers;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.WsClient;
+import org.sonarqube.ws.client.WsResponse;
+import org.sonarqube.ws.client.component.SearchProjectsRequest;
+import org.sonarqube.ws.client.project.CreateRequest;
+import org.sonarqube.ws.client.project.DeleteRequest;
+import org.sonarqube.ws.client.project.SearchWsRequest;
+import util.ItUtils;
+
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static util.ItUtils.getComponent;
+import static util.ItUtils.projectDir;
+
+public class ProjectDeletionTest {
+
+  @ClassRule
+  public static final Orchestrator orchestrator = ProjectSuite.ORCHESTRATOR;
+
+  @Rule
+  public TestRule safeguard = new DisableOnDebug(Timeout.seconds(300));
+
+  @Rule
+  public Tester tester = new Tester(orchestrator).setElasticsearchHttpPort(ProjectSuite.SEARCH_HTTP_PORT);
+
+  @Test
+  public void delete_project_by_web_service() {
+    String projectKey = "sample";
+    String fileKey = "sample:src/main/xoo/sample/Sample.xoo";
+
+    analyzeXooSample();
+    assertThat(getComponent(orchestrator, projectKey)).isNotNull();
+    assertThat(getComponent(orchestrator, fileKey)).isNotNull();
+
+    // fail to delete a file
+    ItUtils.expectBadRequestError(() -> executeDeleteRequest(tester.wsClient(), fileKey));
+
+    // fail if anonymous
+    ItUtils.expectUnauthorizedError(() -> executeDeleteRequest(tester.asAnonymous().wsClient(), projectKey));
+
+    // fail if insufficient privilege
+    WsUsers.CreateWsResponse.User user = tester.users().generate();
+    ItUtils.expectForbiddenError(() -> executeDeleteRequest(tester.as(user.getLogin()).wsClient(), projectKey));
+
+    // succeed to delete if administrator
+    executeDeleteRequest(tester.wsClient(), projectKey);
+    assertThat(getComponent(orchestrator, "sample")).isNull();
+    assertThat(getComponent(orchestrator, "sample:src/main/xoo/sample/Sample.xoo")).isNull();
+  }
+
+  @Test
+  public void deletion_removes_project_from_search_engines() {
+    Organizations.Organization organization = tester.organizations().generate();
+    Project project1 = createProject(organization, "one", "Foo");
+    Project project2 = createProject(organization, "two", "Bar");
+    assertThatProjectIsSearchable(organization, "Foo");
+    assertThatProjectIsSearchable(organization, "Bar");
+
+    deleteProject(project1);
+    assertThatProjectIsNotSearchable(organization, project1.getName());
+    assertThatProjectIsSearchable(organization, project2.getName());
+
+    deleteProject(project2);
+    assertThatProjectIsNotSearchable(organization, project1.getName());
+    assertThatProjectIsNotSearchable(organization, project2.getName());
+  }
+
+  @Test
+  public void indexing_errors_are_recovered_asynchronously_when_deleting_project() throws Exception {
+    Organizations.Organization organization = tester.organizations().generate();
+    Project project = createProject(organization, "one", "Foo");
+
+    tester.elasticsearch().lockWrites("components");
+    tester.elasticsearch().lockWrites("projectmeasures");
+    deleteProject(project);
+    // WS reloads from database the results returned by Elasticsearch. That's
+    // why the project does not appear in search engine.
+    // However this test is still useful to verify that WS do not
+    // fail during this Elasticsearch inconsistency.
+    assertThatProjectIsNotSearchable(organization, project.getName());
+
+    tester.elasticsearch().unlockWrites("components");
+    tester.elasticsearch().unlockWrites("projectmeasures");
+    // TODO verify that recovery daemon successfully updated indices
+  }
+
+  @Test
+  public void bulk_deletion_removes_projects_from_search_engines() {
+    Organizations.Organization organization = tester.organizations().generate();
+    Project project1 = createProject(organization, "one", "Foo");
+    Project project2 = createProject(organization, "two", "Bar");
+    Project project3 = createProject(organization, "three", "Baz");
+
+    bulkDeleteProjects(organization, project1, project3);
+    assertThatProjectIsNotSearchable(organization, project1.getName());
+    assertThatProjectIsSearchable(organization, project2.getName());
+    assertThatProjectIsNotSearchable(organization, project3.getName());
+  }
+
+  @Test
+  public void indexing_errors_are_recovered_asynchronously_when_bulk_deleting_projects() throws Exception {
+    Organizations.Organization organization = tester.organizations().generate();
+    Project project1 = createProject(organization, "one", "Foo");
+    Project project2 = createProject(organization, "two", "Bar");
+    Project project3 = createProject(organization, "three", "Baz");
+
+    tester.elasticsearch().lockWrites("components");
+    tester.elasticsearch().lockWrites("projectmeasures");
+    bulkDeleteProjects(organization, project1, project3);
+
+    // WS reloads from database the results returned by Elasticsearch. That's
+    // why the project does not appear in search engine.
+    // However this test is still useful to verify that WS do not
+    // fail during this Elasticsearch inconsistency.
+    assertThatProjectIsNotSearchable(organization, project1.getName());
+    assertThatProjectIsSearchable(organization, project2.getName());
+    assertThatProjectIsNotSearchable(organization, project3.getName());
+
+    tester.elasticsearch().unlockWrites("components");
+    tester.elasticsearch().unlockWrites("projectmeasures");
+    // TODO verify that recovery daemon successfully updated indices
+  }
+
+  private void deleteProject(Project project) {
+    tester.wsClient().projects().delete(DeleteRequest.builder().setKey(project.getKey()).build());
+  }
+
+  private void bulkDeleteProjects(Organizations.Organization organization, Project... projects) {
+    SearchWsRequest request = SearchWsRequest.builder()
+      .setOrganization(organization.getKey())
+      .setProjects(Arrays.stream(projects).map(Project::getKey).collect(Collectors.toList()))
+      .build();
+    tester.wsClient().projects().bulkDelete(request);
+  }
+
+  private Project createProject(Organizations.Organization organization, String key, String name) {
+    CreateRequest createRequest = CreateRequest.builder().setKey(key).setName(name).setOrganization(organization.getKey()).build();
+    return tester.wsClient().projects().create(createRequest).getProject();
+  }
+
+  private void assertThatProjectIsSearchable(Organizations.Organization organization, String name) {
+    assertThat(isInProjectsSearch(organization, name)).isTrue();
+    assertThat(isInComponentSearchProjects(name)).isTrue();
+    assertThat(isInComponentSuggestions(name)).isTrue();
+  }
+
+  private void assertThatProjectIsNotSearchable(Organizations.Organization organization, String name) {
+    assertThat(isInProjectsSearch(organization, name)).isFalse();
+    assertThat(isInComponentSearchProjects(name)).isFalse();
+    assertThat(isInComponentSuggestions(name)).isFalse();
+  }
+
+  /**
+   * Projects administration page - uses database
+   */
+  private boolean isInProjectsSearch(Organizations.Organization organization, String name) {
+    WsProjects.SearchWsResponse response = tester.wsClient().projects().search(
+      SearchWsRequest.builder().setOrganization(organization.getKey()).setQuery(name).setQualifiers(singletonList("TRK")).build());
+    return response.getComponentsCount() > 0;
+  }
+
+  /**
+   * Projects page - api/components/search_projects - uses ES + DB
+   */
+  private boolean isInComponentSearchProjects(String name) {
+    WsComponents.SearchProjectsWsResponse response = tester.wsClient().components().searchProjects(
+      SearchProjectsRequest.builder().setFilter("query=\"" + name + "\"").build());
+    return response.getComponentsCount() > 0;
+  }
+
+  /**
+   * Top-right search engine - api/components/suggestions - uses ES + DB
+   */
+  private boolean isInComponentSuggestions(String name) {
+    GetRequest request = new GetRequest("api/components/suggestions").setParam("s", name);
+    WsResponse response = tester.wsClient().wsConnector().call(request);
+    Map<String, Object> json = ItUtils.jsonToMap(response.content());
+    Collection<Map<String, Object>> results = (Collection<Map<String, Object>>) json.get("results");
+    Collection items = results.stream()
+      .filter(map -> "TRK".equals(map.get("q")))
+      .map(map -> (Collection) map.get("items"))
+      .findFirst()
+      .orElseThrow(() -> new IllegalStateException("missing field results/[q=TRK]"));
+    return !items.isEmpty();
+  }
+
+  private void analyzeXooSample() {
+    SonarScanner build = SonarScanner.create(projectDir("shared/xoo-sample"));
+    orchestrator.executeBuild(build);
+  }
+
+  private static void executeDeleteRequest(WsClient wsClient, String key) {
+    wsClient.projects().delete(DeleteRequest.builder().setKey(key).build());
+  }
+}
diff --git a/tests/src/test/java/org/sonarqube/tests/project/ProjectFilterTest.java b/tests/src/test/java/org/sonarqube/tests/project/ProjectFilterTest.java
new file mode 100644 (file)
index 0000000..6991386
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * 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.tests.project;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.assertj.core.groups.Tuple;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonarqube.qa.util.Tester;
+import org.sonarqube.ws.Common;
+import org.sonarqube.ws.Organizations.Organization;
+import org.sonarqube.ws.WsComponents.Component;
+import org.sonarqube.ws.WsComponents.SearchProjectsWsResponse;
+import org.sonarqube.ws.client.component.SearchProjectsRequest;
+import org.sonarqube.ws.client.project.CreateRequest;
+
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.groups.Tuple.tuple;
+import static util.ItUtils.concat;
+import static util.ItUtils.newProjectKey;
+import static util.ItUtils.projectDir;
+import static util.ItUtils.restoreProfile;
+import static util.ItUtils.sanitizeTimezones;
+
+/**
+ * Tests WS api/components/search_projects
+ */
+public class ProjectFilterTest {
+
+  @ClassRule
+  public static Orchestrator orchestrator = ProjectSuite.ORCHESTRATOR;
+
+  @Rule
+  public Tester tester = new Tester(orchestrator);
+
+  private Organization organization;
+
+  @Before
+  public void setUp() {
+    organization = tester.organizations().generate();
+    restoreProfile(orchestrator, ProjectFilterTest.class.getResource("/projectSearch/SearchProjectsTest/with-many-rules.xml"), organization.getKey());
+  }
+
+  @Test
+  public void filter_projects_by_measure_values() throws Exception {
+    String projectKey = newProjectKey();
+    analyzeProject(projectKey, "shared/xoo-sample");
+
+    verifyFilterMatches(projectKey, "ncloc > 1");
+    verifyFilterMatches(projectKey, "ncloc > 1 and duplicated_lines_density <= 100");
+    verifyFilterDoesNotMatch("ncloc <= 1");
+  }
+
+  @Test
+  public void find_projects_with_no_data() throws Exception {
+    String projectKey = newProjectKey();
+    analyzeProject(projectKey, "shared/xoo-sample");
+
+    verifyFilterMatches(projectKey, "coverage = NO_DATA");
+    verifyFilterDoesNotMatch("ncloc = NO_DATA");
+  }
+
+  @Test
+  public void provisioned_projects_should_be_included_to_results() throws Exception {
+    String projectKey = newProjectKey();
+    tester.wsClient().projects().create(CreateRequest.builder().setKey(projectKey).setName(projectKey).setOrganization(organization.getKey()).build());
+
+    SearchProjectsWsResponse response = searchProjects(SearchProjectsRequest.builder().setOrganization(organization.getKey()).build());
+
+    assertThat(response.getComponentsList()).extracting(Component::getKey).containsOnly(projectKey);
+  }
+
+  @Test
+  public void return_leak_period_date() throws Exception {
+    tester.settings().setGlobalSettings("sonar.leak.period", "previous_version");
+    // This project has a leak period
+    String projectKey1 = newProjectKey();
+    analyzeProject(projectKey1, "shared/xoo-sample", "sonar.projectDate", "2016-12-31");
+    analyzeProject(projectKey1, "shared/xoo-sample");
+    // This project has only one analysis, so no leak period
+    String projectKey2 = newProjectKey();
+    analyzeProject(projectKey2, "shared/xoo-sample");
+    // This project is provisioned, so has no leak period
+    String projectKey3 = newProjectKey();
+    tester.wsClient().projects().create(CreateRequest.builder().setKey(projectKey3).setName(projectKey3).setOrganization(organization.getKey()).build());
+
+    SearchProjectsWsResponse response = searchProjects(
+      SearchProjectsRequest.builder().setAdditionalFields(singletonList("leakPeriodDate")).setOrganization(organization.getKey()).build());
+
+    assertThat(response.getComponentsList()).extracting(Component::getKey, Component::hasLeakPeriodDate)
+      .containsOnly(
+        tuple(projectKey1, true),
+        tuple(projectKey2, false),
+        tuple(projectKey3, false));
+    Component project1 = response.getComponentsList().stream().filter(component -> component.getKey().equals(projectKey1)).findFirst()
+      .orElseThrow(() -> new IllegalStateException("Project1 is not found"));
+    assertThat(sanitizeTimezones(project1.getLeakPeriodDate())).isEqualTo("2016-12-31T00:00:00+0000");
+  }
+
+  @Test
+  public void filter_by_text_query() throws IOException {
+    analyzeProject("project1", "shared/xoo-sample", "sonar.projectName", "apachee");
+    analyzeProject("project2", "shared/xoo-sample", "sonar.projectName", "Apache");
+    analyzeProject("project3", "shared/xoo-multi-modules-sample", "sonar.projectName", "Apache Foundation");
+    analyzeProject("project4", "shared/xoo-multi-modules-sample", "sonar.projectName", "Windows");
+
+    // Search only by text query
+    assertThat(searchProjects("query = \"apache\"").getComponentsList()).extracting(Component::getKey).containsExactly("project2", "project3", "project1");
+    assertThat(searchProjects("query = \"pAch\"").getComponentsList()).extracting(Component::getKey).containsExactly("project2", "project3", "project1");
+    assertThat(searchProjects("query = \"hee\"").getComponentsList()).extracting(Component::getKey).containsExactly("project1");
+    assertThat(searchProjects("query = \"project1\"").getComponentsList()).extracting(Component::getKey).containsExactly("project1");
+    assertThat(searchProjects("query = \"unknown\"").getComponentsList()).isEmpty();
+
+    // Search by metric criteria and text query
+    assertThat(searchProjects(SearchProjectsRequest.builder().setFilter("query = \"pAch\" AND ncloc > 50").build()).getComponentsList())
+      .extracting(Component::getKey).containsExactly("project3");
+    assertThat(searchProjects(SearchProjectsRequest.builder().setFilter("query = \"nd\" AND ncloc > 50").build()).getComponentsList())
+      .extracting(Component::getKey).containsExactly("project3", "project4");
+    assertThat(searchProjects(SearchProjectsRequest.builder().setFilter("query = \"unknown\" AND ncloc > 50").build()).getComponentsList()).isEmpty();
+    ;
+
+    // Check facets
+    assertThat(searchProjects(SearchProjectsRequest.builder().setFilter("query = \"apache\"").setFacets(singletonList("ncloc")).build()).getFacets().getFacets(0).getValuesList())
+      .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
+      .containsOnly(tuple("*-1000.0", 3L), tuple("1000.0-10000.0", 0L), tuple("10000.0-100000.0", 0L), tuple("100000.0-500000.0", 0L), tuple("500000.0-*", 0L));
+    assertThat(searchProjects(SearchProjectsRequest.builder().setFilter("query = \"unknown\"").setFacets(singletonList("ncloc")).build()).getFacets().getFacets(0)
+      .getValuesList()).extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
+        .containsOnly(tuple("*-1000.0", 0L), tuple("1000.0-10000.0", 0L), tuple("10000.0-100000.0", 0L), tuple("100000.0-500000.0", 0L), tuple("500000.0-*", 0L));
+  }
+
+  @Test
+  public void should_return_facets() throws Exception {
+    analyzeProject(newProjectKey(), "shared/xoo-sample");
+    analyzeProject(newProjectKey(), "shared/xoo-multi-modules-sample");
+
+    SearchProjectsWsResponse response = searchProjects(SearchProjectsRequest.builder().setOrganization(organization.getKey()).setFacets(asList(
+      "alert_status",
+      "coverage",
+      "duplicated_lines_density",
+      "languages",
+      "ncloc",
+      "reliability_rating",
+      "security_rating",
+      "sqale_rating",
+      "tags")).build());
+
+    checkFacet(response, "alert_status",
+      tuple("OK", 2L),
+      tuple("WARN", 0L),
+      tuple("ERROR", 0L));
+    checkFacet(response, "coverage",
+      tuple("NO_DATA", 2L),
+      tuple("*-30.0", 0L),
+      tuple("30.0-50.0", 0L),
+      tuple("50.0-70.0", 0L),
+      tuple("70.0-80.0", 0L),
+      tuple("80.0-*", 0L));
+    checkFacet(response, "duplicated_lines_density",
+      tuple("NO_DATA", 0L),
+      tuple("*-3.0", 2L),
+      tuple("3.0-5.0", 0L),
+      tuple("5.0-10.0", 0L),
+      tuple("10.0-20.0", 0L),
+      tuple("20.0-*", 0L));
+    checkFacet(response, "languages",
+      tuple("xoo", 2L));
+    checkFacet(response, "ncloc",
+      tuple("*-1000.0", 2L),
+      tuple("1000.0-10000.0", 0L),
+      tuple("10000.0-100000.0", 0L),
+      tuple("100000.0-500000.0", 0L),
+      tuple("500000.0-*", 0L));
+    checkFacet(response, "reliability_rating",
+      tuple("1", 2L),
+      tuple("2", 0L),
+      tuple("3", 0L),
+      tuple("4", 0L),
+      tuple("5", 0L));
+    checkFacet(response, "security_rating",
+      tuple("1", 2L),
+      tuple("2", 0L),
+      tuple("3", 0L),
+      tuple("4", 0L),
+      tuple("5", 0L));
+    checkFacet(response, "sqale_rating",
+      tuple("1", 0L),
+      tuple("2", 0L),
+      tuple("3", 0L),
+      tuple("4", 2L),
+      tuple("5", 0L));
+    checkFacet(response, "tags");
+  }
+
+  @Test
+  public void should_return_facets_on_leak() throws Exception {
+    tester.settings().setGlobalSettings("sonar.leak.period", "previous_version");
+    // This project has no duplication on new code
+    String projectKey1 = newProjectKey();
+    analyzeProject(projectKey1, "shared/xoo-sample", "sonar.projectDate", "2016-12-31");
+    analyzeProject(projectKey1, "shared/xoo-sample");
+    // This project has 0% duplication on new code
+    String projectKey2 = newProjectKey();
+    analyzeProject(projectKey2, "projectSearch/xoo-history-v1", "sonar.projectDate", "2016-12-31");
+    analyzeProject(projectKey2, "projectSearch/xoo-history-v2");
+
+    SearchProjectsWsResponse response = searchProjects(SearchProjectsRequest.builder().setOrganization(organization.getKey()).setFacets(asList(
+      "new_reliability_rating", "new_security_rating", "new_maintainability_rating", "new_coverage", "new_duplicated_lines_density", "new_lines")).build());
+
+    checkFacet(response, "new_reliability_rating",
+      tuple("1", 2L),
+      tuple("2", 0L),
+      tuple("3", 0L),
+      tuple("4", 0L),
+      tuple("5", 0L));
+    checkFacet(response, "new_security_rating",
+      tuple("1", 2L),
+      tuple("2", 0L),
+      tuple("3", 0L),
+      tuple("4", 0L),
+      tuple("5", 0L));
+    checkFacet(response, "new_maintainability_rating",
+      tuple("1", 2L),
+      tuple("2", 0L),
+      tuple("3", 0L),
+      tuple("4", 0L),
+      tuple("5", 0L));
+    checkFacet(response, "new_coverage",
+      tuple("NO_DATA", 2L),
+      tuple("*-30.0", 0L),
+      tuple("30.0-50.0", 0L),
+      tuple("50.0-70.0", 0L),
+      tuple("70.0-80.0", 0L),
+      tuple("80.0-*", 0L));
+    checkFacet(response, "new_duplicated_lines_density",
+      tuple("NO_DATA", 1L),
+      tuple("*-3.0", 1L),
+      tuple("3.0-5.0", 0L),
+      tuple("5.0-10.0", 0L),
+      tuple("10.0-20.0", 0L),
+      tuple("20.0-*", 0L));
+    checkFacet(response, "new_lines",
+      tuple("*-1000.0", 1L),
+      tuple("1000.0-10000.0", 0L),
+      tuple("10000.0-100000.0", 0L),
+      tuple("100000.0-500000.0", 0L),
+      tuple("500000.0-*", 0L));
+  }
+
+  private void checkFacet(SearchProjectsWsResponse response, String facetKey, Tuple... values) {
+    Common.Facet facet = response.getFacets().getFacetsList().stream().filter(f -> f.getProperty().equals(facetKey)).findAny().get();
+    assertThat(facet.getValuesList()).extracting(Common.FacetValue::getVal, Common.FacetValue::getCount).containsExactlyInAnyOrder(values);
+  }
+
+  private void analyzeProject(String projectKey, String relativePath, String... properties) {
+    List<String> keyValueProperties = new ArrayList<>(asList(
+      "sonar.projectKey", projectKey,
+      "sonar.organization", organization.getKey(),
+      "sonar.profile", "with-many-rules",
+      "sonar.login", "admin", "sonar.password", "admin",
+      "sonar.scm.disabled", "false"));
+    orchestrator.executeBuild(SonarScanner.create(projectDir(relativePath), concat(keyValueProperties.toArray(new String[0]), properties)));
+  }
+
+  private SearchProjectsWsResponse searchProjects(String filter) throws IOException {
+    return searchProjects(SearchProjectsRequest.builder().setOrganization(organization.getKey()).setFilter(filter).build());
+  }
+
+  private SearchProjectsWsResponse searchProjects(SearchProjectsRequest request) throws IOException {
+    return tester.wsClient().components().searchProjects(request);
+  }
+
+  private void verifyFilterMatches(String projectKey, String filter) throws IOException {
+    assertThat(searchProjects(filter).getComponentsList()).extracting(Component::getKey).containsOnly(projectKey);
+  }
+
+  private void verifyFilterDoesNotMatch(String filter) throws IOException {
+    assertThat(searchProjects(filter).getComponentsCount()).isZero();
+  }
+}
diff --git a/tests/src/test/java/org/sonarqube/tests/project/ProjectKeyUpdateTest.java b/tests/src/test/java/org/sonarqube/tests/project/ProjectKeyUpdateTest.java
new file mode 100644 (file)
index 0000000..8a2c14f
--- /dev/null
@@ -0,0 +1,284 @@
+/*
+ * 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.tests.project;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import javax.annotation.CheckForNull;
+import org.junit.After;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.DisableOnDebug;
+import org.junit.rules.TestRule;
+import org.junit.rules.Timeout;
+import org.sonarqube.qa.util.Tester;
+import org.sonarqube.ws.Organizations;
+import org.sonarqube.ws.WsComponents;
+import org.sonarqube.ws.WsProjects;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.WsResponse;
+import org.sonarqube.ws.client.component.SearchProjectsRequest;
+import org.sonarqube.ws.client.component.ShowWsRequest;
+import org.sonarqube.ws.client.project.BulkUpdateKeyWsRequest;
+import org.sonarqube.ws.client.project.CreateRequest;
+import org.sonarqube.ws.client.project.UpdateKeyWsRequest;
+import util.ItUtils;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static util.ItUtils.projectDir;
+
+public class ProjectKeyUpdateTest {
+
+  private static final String PROJECT_KEY = "sample";
+
+  @ClassRule
+  public static final Orchestrator orchestrator = ProjectSuite.ORCHESTRATOR;
+
+  @Rule
+  public TestRule safeguard = new DisableOnDebug(Timeout.seconds(300));
+
+  @Rule
+  public Tester tester = new Tester(orchestrator).setElasticsearchHttpPort(ProjectSuite.SEARCH_HTTP_PORT);
+
+  @After
+  public void tearDown() throws Exception {
+    unlockWritesOnProjectIndices();
+  }
+
+  @Test
+  public void update_key() {
+    analyzeXooSample();
+    String newProjectKey = "another_project_key";
+    WsComponents.Component project = tester.wsClient().components().show(new ShowWsRequest().setKey(PROJECT_KEY)).getComponent();
+    assertThat(project.getKey()).isEqualTo(PROJECT_KEY);
+
+    tester.wsClient().projects().updateKey(UpdateKeyWsRequest.builder()
+      .setKey(PROJECT_KEY)
+      .setNewKey(newProjectKey)
+      .build());
+
+    assertThat(tester.wsClient().components().show(new ShowWsRequest().setId(project.getId())).getComponent().getKey()).isEqualTo(newProjectKey);
+  }
+
+  @Test
+  public void bulk_update_key() {
+    analyzeXooSample();
+    String newProjectKey = "another_project_key";
+    WsComponents.Component project = tester.wsClient().components().show(new ShowWsRequest().setKey(PROJECT_KEY)).getComponent();
+    assertThat(project.getKey()).isEqualTo(PROJECT_KEY);
+
+    WsProjects.BulkUpdateKeyWsResponse result = tester.wsClient().projects().bulkUpdateKey(BulkUpdateKeyWsRequest.builder()
+      .setKey(PROJECT_KEY)
+      .setFrom(PROJECT_KEY)
+      .setTo(newProjectKey)
+      .build());
+
+    assertThat(tester.wsClient().components().show(new ShowWsRequest().setId(project.getId())).getComponent().getKey()).isEqualTo(newProjectKey);
+    assertThat(result.getKeysCount()).isEqualTo(1);
+    assertThat(result.getKeys(0))
+      .extracting(WsProjects.BulkUpdateKeyWsResponse.Key::getKey, WsProjects.BulkUpdateKeyWsResponse.Key::getNewKey, WsProjects.BulkUpdateKeyWsResponse.Key::getDuplicate)
+      .containsOnlyOnce(PROJECT_KEY, newProjectKey, false);
+  }
+
+  @Test
+  public void update_key_of_provisioned_project() {
+    Organizations.Organization organization = tester.organizations().generate();
+    WsProjects.CreateWsResponse.Project project = createProject(organization, "one", "Foo");
+
+    updateKey(project, "two");
+
+    assertThat(isProjectInDatabase("one")).isFalse();
+    assertThat(isProjectInDatabase("two")).isTrue();
+    assertThat(isComponentInDatabase("one")).isFalse();
+    assertThat(isComponentInDatabase("two")).isTrue();
+    assertThat(keyInComponentSearchProjects("Foo")).isEqualTo("two");
+    assertThat(keysInComponentSuggestions("Foo")).containsExactly("two");
+  }
+
+  @Test
+  public void recover_indexing_errors_when_updating_key_of_provisioned_project() throws Exception {
+    Organizations.Organization organization = tester.organizations().generate();
+    WsProjects.CreateWsResponse.Project project = createProject(organization, "one", "Foo");
+
+    lockWritesOnProjectIndices();
+
+    updateKey(project, "two");
+
+    assertThat(isProjectInDatabase("one")).isFalse();
+
+    // WS gets the list of projects from ES then reloads projects from db.
+    // That's why keys in WS responses are correct.
+    assertThat(isProjectInDatabase("one")).isFalse();
+    assertThat(isProjectInDatabase("two")).isTrue();
+    assertThat(keyInComponentSearchProjects("Foo")).isEqualTo("two");
+    assertThat(keysInComponentSuggestions("Foo")).containsExactly("two");
+
+    // however searching by key is inconsistent
+    assertThat(keyInComponentSearchProjects("one")).isEqualTo("two");
+    assertThat(keysInComponentSuggestions("one")).containsExactly("two");
+    assertThat(keyInComponentSearchProjects("two")).isNull();
+    assertThat(keysInComponentSuggestions("two")).isEmpty();
+
+    unlockWritesOnProjectIndices();
+
+    boolean recovered = false;
+    while (!recovered) {
+      // recovery daemon runs every second, see Category6Suite
+      Thread.sleep(1_000L);
+      recovered = keyInComponentSearchProjects("one") == null &&
+        keysInComponentSuggestions("one").isEmpty() &&
+        "two".equals(keyInComponentSearchProjects("two")) &&
+        keysInComponentSuggestions("two").contains("two");
+    }
+  }
+
+  @Test
+  public void update_key_of_module() {
+    Organizations.Organization organization = tester.organizations().generate();
+    orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-multi-modules-sample"),
+      "sonar.organization", organization.getKey(),
+      "sonar.login", "admin", "sonar.password", "admin"));
+
+    String initialKey = "com.sonarsource.it.samples:multi-modules-sample:module_a";
+    String newKey = "com.sonarsource.it.samples:multi-modules-sample:module_c";
+
+    updateKey(initialKey, newKey);
+
+    assertThat(isComponentInDatabase(initialKey)).isFalse();
+    assertThat(isComponentInDatabase(newKey)).isTrue();
+    // suggestions engine ignores one-character words, so we can't search for "Module A"
+    assertThat(keysInComponentSuggestions("Module"))
+      .contains(newKey)
+      .doesNotContain(initialKey);
+    assertThat(keysInComponentSuggestions(newKey))
+      .contains(newKey)
+      .doesNotContain(initialKey);
+    assertThat(keysInComponentSuggestions(initialKey)).isEmpty();
+
+  }
+
+  @Test
+  public void recover_indexing_errors_when_updating_key_of_module() throws Exception {
+    Organizations.Organization organization = tester.organizations().generate();
+    orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-multi-modules-sample"),
+      "sonar.organization", organization.getKey(),
+      "sonar.login", "admin", "sonar.password", "admin"));
+
+    String initialKey = "com.sonarsource.it.samples:multi-modules-sample:module_a";
+    String newKey = "com.sonarsource.it.samples:multi-modules-sample:module_c";
+
+    lockWritesOnProjectIndices();
+    updateKey(initialKey, newKey);
+
+    // api/components/search loads keys from db, so results are consistent
+    assertThat(isComponentInDatabase(initialKey)).isFalse();
+    assertThat(isComponentInDatabase(newKey)).isTrue();
+
+    // key in result of suggestion engine is loaded from db, so results are ok when searching for unchanged name
+    assertThat(keysInComponentSuggestions("Module"))
+      .contains(newKey)
+      .doesNotContain(initialKey);
+
+    // but searching for new key does not work
+    assertThat(keysInComponentSuggestions(newKey)).isEmpty();
+    assertThat(keysInComponentSuggestions(initialKey))
+      .isNotEmpty()
+      .contains(newKey /* the returned key is loaded from db, so it's correct */);
+
+    unlockWritesOnProjectIndices();
+
+    boolean recovered = false;
+    while (!recovered) {
+      // recovery daemon runs every second, see Category6Suite
+      Thread.sleep(1_000L);
+      recovered = keysInComponentSuggestions(newKey).contains(newKey) && keysInComponentSuggestions(initialKey).isEmpty();
+    }
+
+  }
+
+  private void lockWritesOnProjectIndices() throws Exception {
+    tester.elasticsearch().lockWrites("components");
+    tester.elasticsearch().lockWrites("projectmeasures");
+  }
+
+  private void unlockWritesOnProjectIndices() throws Exception {
+    tester.elasticsearch().unlockWrites("components");
+    tester.elasticsearch().unlockWrites("projectmeasures");
+  }
+
+  private void updateKey(WsProjects.CreateWsResponse.Project project, String newKey) {
+    tester.wsClient().projects().updateKey(UpdateKeyWsRequest.builder().setKey(project.getKey()).setNewKey(newKey).build());
+  }
+
+  private void updateKey(String initialKey, String newKey) {
+    tester.wsClient().projects().updateKey(UpdateKeyWsRequest.builder().setKey(initialKey).setNewKey(newKey).build());
+  }
+
+  private WsProjects.CreateWsResponse.Project createProject(Organizations.Organization organization, String key, String name) {
+    CreateRequest createRequest = CreateRequest.builder().setKey(key).setName(name).setOrganization(organization.getKey()).build();
+    return tester.wsClient().projects().create(createRequest).getProject();
+  }
+
+  private boolean isProjectInDatabase(String projectKey) {
+    return orchestrator.getDatabase().countSql(String.format("select count(id) from projects where qualifier='TRK' and kee='%s'", projectKey)) == 1L;
+  }
+
+  private boolean isComponentInDatabase(String componentKey) {
+    return orchestrator.getDatabase().countSql(String.format("select count(id) from projects where kee='%s'", componentKey)) == 1L;
+  }
+
+  /**
+   * Projects page - api/components/search_projects - uses ES + DB
+   */
+  @CheckForNull
+  private String keyInComponentSearchProjects(String name) {
+    WsComponents.SearchProjectsWsResponse response = tester.wsClient().components().searchProjects(
+      SearchProjectsRequest.builder().setFilter("query=\"" + name + "\"").build());
+    if (response.getComponentsCount() > 0) {
+      return response.getComponents(0).getKey();
+    }
+    return null;
+  }
+
+  /**
+   * Top-right search engine - api/components/suggestions - uses ES + DB
+   */
+  private List<String> keysInComponentSuggestions(String name) {
+    GetRequest request = new GetRequest("api/components/suggestions").setParam("s", name);
+    WsResponse response = tester.wsClient().wsConnector().call(request);
+    Map<String, Object> json = ItUtils.jsonToMap(response.content());
+    Collection<Map<String, Object>> results = (Collection<Map<String, Object>>) json.get("results");
+    return results.stream()
+      .filter(map -> "TRK".equals(map.get("q")) || "BRC".equals(map.get("q")))
+      .flatMap(map -> ((Collection<Map<String, Object>>) map.get("items")).stream())
+      .map(map -> (String) map.get("key"))
+      .collect(Collectors.toList());
+  }
+
+  private void analyzeXooSample() {
+    SonarScanner build = SonarScanner.create(projectDir("shared/xoo-sample"));
+    orchestrator.executeBuild(build);
+  }
+}
diff --git a/tests/src/test/java/org/sonarqube/tests/project/ProjectLeakPageTest.java b/tests/src/test/java/org/sonarqube/tests/project/ProjectLeakPageTest.java
new file mode 100644 (file)
index 0000000..1b0e145
--- /dev/null
@@ -0,0 +1,105 @@
+/*
+ * 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.tests.project;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import java.util.ArrayList;
+import java.util.List;
+import javax.annotation.Nullable;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonarqube.qa.util.Tester;
+import org.sonarqube.qa.util.pageobjects.projects.ProjectsPage;
+import org.sonarqube.ws.Organizations.Organization;
+
+import static com.codeborne.selenide.WebDriverRunner.url;
+import static java.util.Arrays.asList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static util.ItUtils.newProjectKey;
+import static util.ItUtils.projectDir;
+import static util.ItUtils.restoreProfile;
+
+public class ProjectLeakPageTest {
+
+  @ClassRule
+  public static Orchestrator orchestrator = ProjectSuite.ORCHESTRATOR;
+
+  @Rule
+  public Tester tester = new Tester(orchestrator);
+
+  private Organization organization;
+
+  @Before
+  public void setUp() {
+    tester.settings().setGlobalSettings("sonar.leak.period", "previous_version");
+    organization = tester.organizations().generate();
+    restoreProfile(orchestrator, ProjectLeakPageTest.class.getResource("/projectSearch/SearchProjectsTest/with-many-rules.xml"), organization.getKey());
+  }
+
+  @Test
+  public void should_display_leak_information() {
+    // This project has 0% duplication on new code
+    String projectKey2 = newProjectKey();
+    analyzeProject(projectKey2, "projectSearch/xoo-history-v1", "2016-12-31");
+    analyzeProject(projectKey2, "projectSearch/xoo-history-v2", null);
+
+    // This project has no duplication on new code
+    String projectKey1 = newProjectKey();
+    analyzeProject(projectKey1, "shared/xoo-sample", "2016-12-31");
+    analyzeProject(projectKey1, "shared/xoo-sample", null);
+
+    // Check the facets and project cards
+    ProjectsPage page = tester.openBrowser().openProjects(organization.getKey());
+    page.changePerspective("Leak");
+    assertThat(url()).endsWith("/projects?view=leak");
+    page.shouldHaveTotal(2);
+    page.getProjectByKey(projectKey2)
+      .shouldHaveMeasure("new_reliability_rating", "0A")
+      .shouldHaveMeasure("new_security_rating", "0A")
+      .shouldHaveMeasure("new_maintainability_rating", "17A")
+      .shouldHaveMeasure("new_coverage", "–")
+      .shouldHaveMeasure("new_duplicated_lines_density", "0.0%")
+      .shouldHaveMeasure("new_lines", "17");
+    page.getFacetByProperty("new_duplications")
+      .shouldHaveValue("1", "1")
+      .shouldHaveValue("2", "0")
+      .shouldHaveValue("3", "0")
+      .shouldHaveValue("4", "0")
+      .shouldHaveValue("5", "0")
+      .shouldHaveValue("6", "1");
+  }
+
+  private void analyzeProject(String projectKey, String relativePath, @Nullable String analysisDate) {
+    List<String> keyValueProperties = new ArrayList<>(asList(
+      "sonar.projectKey", projectKey,
+      "sonar.organization", organization.getKey(),
+      "sonar.profile", "with-many-rules",
+      "sonar.login", "admin", "sonar.password", "admin",
+      "sonar.scm.disabled", "false"));
+    if (analysisDate != null) {
+      keyValueProperties.add("sonar.projectDate");
+      keyValueProperties.add(analysisDate);
+    }
+    orchestrator.executeBuild(SonarScanner.create(projectDir(relativePath), keyValueProperties.toArray(new String[0])));
+  }
+}
diff --git a/tests/src/test/java/org/sonarqube/tests/project/ProjectLinksTest.java b/tests/src/test/java/org/sonarqube/tests/project/ProjectLinksTest.java
new file mode 100644 (file)
index 0000000..4efc15d
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * 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.tests.project;
+
+import com.codeborne.selenide.Condition;
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import java.util.List;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.sonarqube.qa.util.Tester;
+import org.sonarqube.qa.util.pageobjects.ProjectLinkItem;
+import org.sonarqube.qa.util.pageobjects.ProjectLinksPage;
+import org.sonarqube.ws.WsProjectLinks.CreateWsResponse;
+import org.sonarqube.ws.client.projectlinks.CreateWsRequest;
+import org.sonarqube.ws.client.projectlinks.DeleteWsRequest;
+
+import static com.codeborne.selenide.Condition.text;
+import static com.codeborne.selenide.Selenide.$;
+import static util.ItUtils.projectDir;
+
+public class ProjectLinksTest {
+
+  @ClassRule
+  public static Orchestrator orchestrator = ProjectSuite.ORCHESTRATOR;
+
+  private static Tester tester = new Tester(orchestrator);
+
+  @ClassRule
+  public static RuleChain ruleChain = RuleChain.outerRule(orchestrator).around(tester);
+
+  private long customLinkId;
+  private String adminUser;
+
+  @BeforeClass
+  public static void setUp() {
+    orchestrator.executeBuild(
+      SonarScanner.create(projectDir("shared/xoo-sample"))
+        .setProperty("sonar.links.homepage", "http://example.com"));
+  }
+
+  @Before
+  public void prepare() {
+    customLinkId = Long.parseLong(createCustomLink().getLink().getId());
+    adminUser = tester.users().generateAdministratorOnDefaultOrganization().getLogin();
+  }
+
+  @After
+  public void clean() {
+    deleteLink(customLinkId);
+  }
+
+  @Test
+  public void should_list_links() {
+    ProjectLinksPage page = openLinksPage();
+
+    page.getLinks().shouldHaveSize(2);
+
+    List<ProjectLinkItem> links = page.getLinksAsItems();
+    ProjectLinkItem homepageLink = links.get(0);
+    ProjectLinkItem customLink = links.get(1);
+
+    homepageLink.getName().should(text("Home"));
+    homepageLink.getType().should(text("sonar.links.homepage"));
+    homepageLink.getUrl().should(text("http://example.com"));
+    homepageLink.getDeleteButton().shouldNot(Condition.exist);
+
+    customLink.getName().should(text("Custom"));
+    customLink.getType().shouldNot(Condition.exist);
+    customLink.getUrl().should(text("http://example.org/custom"));
+    customLink.getDeleteButton().shouldBe(Condition.visible);
+  }
+
+  @Test
+  public void should_create_link() {
+    ProjectLinksPage page = openLinksPage();
+
+    page.getLinks().shouldHaveSize(2);
+
+    $("#create-project-link").click();
+    $("#create-link-name").setValue("Test");
+    $("#create-link-url").setValue("http://example.com/test");
+    $("#create-link-confirm").click();
+
+    page.getLinks().shouldHaveSize(3);
+
+    ProjectLinkItem testLink = page.getLinksAsItems().get(2);
+
+    testLink.getName().should(text("Test"));
+    testLink.getType().shouldNot(Condition.exist);
+    testLink.getUrl().should(text("http://example.com/test"));
+    testLink.getDeleteButton().shouldBe(Condition.visible);
+  }
+
+  @Test
+  public void should_delete_link() {
+    ProjectLinksPage page = openLinksPage();
+
+    page.getLinks().shouldHaveSize(2);
+
+    List<ProjectLinkItem> links = page.getLinksAsItems();
+    ProjectLinkItem customLink = links.get(1);
+
+    customLink.getDeleteButton().click();
+    $("#delete-link-confirm")
+      .shouldBe(Condition.visible)
+      .click();
+
+    page.getLinks().shouldHaveSize(1);
+  }
+
+  private CreateWsResponse createCustomLink() {
+    return tester.wsClient().projectLinks().create(new CreateWsRequest()
+      .setProjectKey("sample")
+      .setName("Custom")
+      .setUrl("http://example.org/custom"));
+  }
+
+  private void deleteLink(long id) {
+    try {
+      tester.wsClient().projectLinks().delete(new DeleteWsRequest().setId(id));
+    } catch (Exception e) {
+      // fail silently
+    }
+  }
+
+  private ProjectLinksPage openLinksPage() {
+    return tester
+      .openBrowser()
+      .logIn()
+      .submitCredentials(adminUser, adminUser)
+      .openProjectLinks("sample");
+  }
+}
diff --git a/tests/src/test/java/org/sonarqube/tests/project/ProjectListTest.java b/tests/src/test/java/org/sonarqube/tests/project/ProjectListTest.java
new file mode 100644 (file)
index 0000000..55b1e4e
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * 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.tests.project;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import java.io.IOException;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonarqube.qa.util.Tester;
+import org.sonarqube.ws.client.GetRequest;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static util.ItUtils.projectDir;
+
+public class ProjectListTest {
+
+  @ClassRule
+  public static final Orchestrator orchestrator = ProjectSuite.ORCHESTRATOR;
+
+  @Rule
+  public Tester tester = new Tester(orchestrator);
+
+  /**
+   * SONAR-3105
+   */
+  @Test
+  public void test_projects_index_ws() throws IOException {
+    SonarScanner build = SonarScanner.create(projectDir("shared/xoo-sample"));
+    orchestrator.executeBuild(build);
+
+    String json = tester.wsClient().wsConnector().call(new GetRequest("/api/projects/index?key=sample&versions=true")).content();
+
+    assertThat(json).doesNotContain("error");
+    assertThat(json).contains("sample");
+  }
+
+
+}
diff --git a/tests/src/test/java/org/sonarqube/tests/project/ProjectProvisioningTest.java b/tests/src/test/java/org/sonarqube/tests/project/ProjectProvisioningTest.java
new file mode 100644 (file)
index 0000000..9f9b1a8
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * 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.tests.project;
+
+import com.sonar.orchestrator.Orchestrator;
+import java.util.Collection;
+import java.util.Map;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.DisableOnDebug;
+import org.junit.rules.TestRule;
+import org.junit.rules.Timeout;
+import org.sonarqube.qa.util.Tester;
+import org.sonarqube.ws.Organizations;
+import org.sonarqube.ws.WsComponents;
+import org.sonarqube.ws.WsProjects;
+import org.sonarqube.ws.WsProjects.CreateWsResponse.Project;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.WsResponse;
+import org.sonarqube.ws.client.component.SearchProjectsRequest;
+import org.sonarqube.ws.client.project.CreateRequest;
+import org.sonarqube.ws.client.project.SearchWsRequest;
+import util.ItUtils;
+
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ProjectProvisioningTest {
+
+  @ClassRule
+  public static final Orchestrator orchestrator = ProjectSuite.ORCHESTRATOR;
+
+  @Rule
+  public TestRule safeguard = new DisableOnDebug(Timeout.seconds(300));
+
+  @Rule
+  public Tester tester = new Tester(orchestrator).setElasticsearchHttpPort(ProjectSuite.SEARCH_HTTP_PORT);
+
+  @Test
+  public void provisioned_project_is_available_in_search_engines() {
+    Organizations.Organization organization = tester.organizations().generate();
+
+    createProject(organization, "one", "Foo");
+
+    assertThat(isInProjectsSearch(organization, "Foo")).isTrue();
+    assertThat(isInComponentSearchProjects("Foo")).isTrue();
+    assertThat(isInComponentSuggestions("Foo")).isTrue();
+  }
+
+  @Test
+  public void indexing_errors_are_recovered_asynchronously_when_provisioning_project() throws Exception {
+    tester.elasticsearch().lockWrites("components");
+    tester.elasticsearch().lockWrites("projectmeasures");
+
+    Organizations.Organization organization = tester.organizations().generate();
+    Project project = createProject(organization, "one", "Foo");
+
+    // no ES requests but only DB
+    assertThat(isInProjectsSearch(organization, project.getName())).isTrue();
+
+    // these WS use ES so they are temporarily inconsistent
+    assertThat(isInComponentSearchProjects(project.getName())).isFalse();
+    assertThat(isInComponentSuggestions(project.getName())).isFalse();
+
+    tester.elasticsearch().unlockWrites("components");
+    tester.elasticsearch().unlockWrites("projectmeasures");
+
+    boolean found = false;
+    while (!found) {
+      // recovery daemon runs every second (see Category6Suite)
+      Thread.sleep(1_000L);
+      found = isInComponentSearchProjects(project.getName()) && isInComponentSuggestions(project.getName());
+    }
+  }
+
+  private Project createProject(Organizations.Organization organization, String key, String name) {
+    CreateRequest createRequest = CreateRequest.builder().setKey(key).setName(name).setOrganization(organization.getKey()).build();
+    return tester.wsClient().projects().create(createRequest).getProject();
+  }
+
+  /**
+   * Projects administration page - uses database
+   */
+  private boolean isInProjectsSearch(Organizations.Organization organization, String name) {
+    WsProjects.SearchWsResponse response = tester.wsClient().projects().search(
+      SearchWsRequest.builder().setOrganization(organization.getKey()).setQuery(name).setQualifiers(singletonList("TRK")).build());
+    return response.getComponentsCount() > 0;
+  }
+
+  /**
+   * Projects page - api/components/search_projects - uses ES + DB
+   */
+  private boolean isInComponentSearchProjects(String name) {
+    WsComponents.SearchProjectsWsResponse response = tester.wsClient().components().searchProjects(
+      SearchProjectsRequest.builder().setFilter("query=\"" + name + "\"").build());
+    return response.getComponentsCount() > 0;
+  }
+
+  /**
+   * Top-right search engine - api/components/suggestions - uses ES + DB
+   */
+  private boolean isInComponentSuggestions(String name) {
+    GetRequest request = new GetRequest("api/components/suggestions").setParam("s", name);
+    WsResponse response = tester.wsClient().wsConnector().call(request);
+    Map<String, Object> json = ItUtils.jsonToMap(response.content());
+    Collection<Map<String, Object>> results = (Collection<Map<String, Object>>) json.get("results");
+    Collection items = results.stream()
+      .filter(map -> "TRK".equals(map.get("q")))
+      .map(map -> (Collection) map.get("items"))
+      .findFirst()
+      .orElseThrow(() -> new IllegalStateException("missing field results/[q=TRK]"));
+    return !items.isEmpty();
+  }
+}
diff --git a/tests/src/test/java/org/sonarqube/tests/project/ProjectSearchTest.java b/tests/src/test/java/org/sonarqube/tests/project/ProjectSearchTest.java
new file mode 100644 (file)
index 0000000..69cc1a6
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+ * 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.tests.project;
+
+import com.sonar.orchestrator.Orchestrator;
+import java.util.Date;
+import org.apache.commons.lang.time.DateUtils;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonarqube.qa.util.Tester;
+import org.sonarqube.ws.Organizations;
+import org.sonarqube.ws.WsProjects.CreateWsResponse;
+import org.sonarqube.ws.WsProjects.SearchWsResponse;
+import org.sonarqube.ws.WsProjects.SearchWsResponse.Component;
+import org.sonarqube.ws.client.GetRequest;
+import org.sonarqube.ws.client.project.SearchWsRequest;
+
+import static java.util.Collections.singletonList;
+import static org.assertj.core.api.Assertions.assertThat;
+import static util.ItUtils.formatDate;
+import static util.ItUtils.runProjectAnalysis;
+
+public class ProjectSearchTest {
+
+  @ClassRule
+  public static Orchestrator orchestrator = ProjectSuite.ORCHESTRATOR;
+
+  @Rule
+  public Tester tester = new Tester(orchestrator);
+
+  @Test
+  public void search_old_projects() {
+    Organizations.Organization organization = tester.organizations().generate();
+    CreateWsResponse.Project oldProject = tester.projects().generate(organization);
+    CreateWsResponse.Project recentProject = tester.projects().generate(organization);
+    Date now = new Date();
+    Date oneYearAgo = DateUtils.addDays(now, -365);
+    Date moreThanOneYearAgo = DateUtils.addDays(now, -366);
+
+    analyzeProject(oldProject.getKey(), moreThanOneYearAgo, organization.getKey());
+    analyzeProject(recentProject.getKey(), now, organization.getKey());
+
+    SearchWsResponse result = tester.wsClient().projects().search(SearchWsRequest.builder()
+      .setOrganization(organization.getKey())
+      .setQualifiers(singletonList("TRK"))
+      .setAnalyzedBefore(formatDate(oneYearAgo)).build());
+
+    assertThat(result.getComponentsList()).extracting(Component::getKey).containsExactlyInAnyOrder(oldProject.getKey());
+  }
+
+  @Test
+  public void search_on_key_query_partial_match_case_insensitive() {
+    Organizations.Organization organization = tester.organizations().generate();
+    CreateWsResponse.Project lowerCaseProject = tester.projects().generate(organization, p -> p.setKey("project-key"));
+    CreateWsResponse.Project upperCaseProject = tester.projects().generate(organization, p -> p.setKey("PROJECT-KEY"));
+    CreateWsResponse.Project anotherProject = tester.projects().generate(organization, p -> p.setKey("another-project"));
+
+    analyzeProject(lowerCaseProject.getKey(), organization.getKey());
+    analyzeProject(upperCaseProject.getKey(), organization.getKey());
+    analyzeProject(anotherProject.getKey(), organization.getKey());
+
+    SearchWsResponse result = tester.wsClient().projects().search(SearchWsRequest.builder()
+      .setOrganization(organization.getKey())
+      .setQualifiers(singletonList("TRK"))
+      .setQuery("JeCt-K")
+      .build());
+
+    assertThat(result.getComponentsList()).extracting(Component::getKey)
+      .containsExactlyInAnyOrder(lowerCaseProject.getKey(), upperCaseProject.getKey())
+      .doesNotContain(anotherProject.getKey());
+  }
+
+  @Test
+  public void search_provisioned_projects() {
+    Organizations.Organization organization = tester.organizations().generate();
+    CreateWsResponse.Project firstProvisionedProject = tester.projects().generate(organization);
+    CreateWsResponse.Project secondProvisionedProject = tester.projects().generate(organization);
+    CreateWsResponse.Project analyzedProject = tester.projects().generate(organization);
+
+    analyzeProject(analyzedProject.getKey(), organization.getKey());
+
+    String result = tester.wsClient().wsConnector().call(new GetRequest("api/projects/provisioned")
+      .setParam("organization", organization.getKey()))
+      .failIfNotSuccessful().content();
+   SearchWsResponse searchResult = tester.wsClient().projects().search(SearchWsRequest.builder()
+     .setQualifiers(singletonList("TRK"))
+     .setOrganization(organization.getKey())
+     .setOnProvisionedOnly(true).build());
+
+    assertThat(result).contains(firstProvisionedProject.getKey(), secondProvisionedProject.getKey()).doesNotContain(analyzedProject.getKey());
+    assertThat(searchResult.getComponentsList()).extracting(Component::getKey)
+      .containsOnly(firstProvisionedProject.getKey(), secondProvisionedProject.getKey())
+      .doesNotContain(analyzedProject.getKey());
+  }
+
+  private void analyzeProject(String projectKey, Date analysisDate, String organizationKey) {
+    runProjectAnalysis(orchestrator, "shared/xoo-sample",
+      "sonar.organization", organizationKey,
+      "sonar.projectKey", projectKey,
+      "sonar.projectDate", formatDate(analysisDate),
+      "sonar.login", "admin",
+      "sonar.password", "admin");
+  }
+
+  private void analyzeProject(String projectKey, String organizationKey) {
+    analyzeProject(projectKey, new Date(), organizationKey);
+  }
+}
diff --git a/tests/src/test/java/org/sonarqube/tests/project/ProjectSuite.java b/tests/src/test/java/org/sonarqube/tests/project/ProjectSuite.java
new file mode 100644 (file)
index 0000000..58dd29c
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * 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.tests.project;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.util.NetworkUtils;
+import java.net.InetAddress;
+import org.junit.ClassRule;
+import org.junit.runner.RunWith;
+import org.junit.runners.Suite;
+
+import static util.ItUtils.xooPlugin;
+
+@RunWith(Suite.class)
+@Suite.SuiteClasses({
+  ProjectBulkDeletionTest.class,
+  ProjectBulkDeletionPageTest.class,
+  ProjectDeletionTest.class,
+  ProjectFilterTest.class,
+  ProjectKeyUpdateTest.class,
+  ProjectLeakPageTest.class,
+  ProjectLinksTest.class,
+  ProjectListTest.class,
+  ProjectsPageTest.class,
+  ProjectProvisioningTest.class,
+  ProjectSearchTest.class,
+  ProjectVisibilityPageTest.class
+})
+public class ProjectSuite {
+  public static final int SEARCH_HTTP_PORT = NetworkUtils.getNextAvailablePort(InetAddress.getLoopbackAddress());
+
+  @ClassRule
+  public static final Orchestrator ORCHESTRATOR = Orchestrator.builderEnv()
+    // reduce memory for Elasticsearch
+    .setServerProperty("sonar.search.javaOpts", "-Xms128m -Xmx128m")
+    // for ES resiliency tests
+    .setServerProperty("sonar.search.httpPort", "" + SEARCH_HTTP_PORT)
+    .setServerProperty("sonar.search.recovery.delayInMs", "1000")
+    .setServerProperty("sonar.search.recovery.minAgeInMs", "3000")
+    .setServerProperty("sonar.notifications.delay", "1")
+
+    .addPlugin(xooPlugin())
+
+    .build();
+
+}
diff --git a/tests/src/test/java/org/sonarqube/tests/project/ProjectVisibilityPageTest.java b/tests/src/test/java/org/sonarqube/tests/project/ProjectVisibilityPageTest.java
new file mode 100644 (file)
index 0000000..0c2c745
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * 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.tests.project;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonarqube.qa.util.Tester;
+import org.sonarqube.qa.util.pageobjects.ProjectsManagementPage;
+import org.sonarqube.ws.WsComponents;
+import org.sonarqube.ws.client.component.SearchProjectsRequest;
+import org.sonarqube.ws.client.permission.RemoveGroupWsRequest;
+import org.sonarqube.ws.client.project.UpdateVisibilityRequest;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static util.ItUtils.newAdminWsClient;
+import static util.ItUtils.projectDir;
+
+public class ProjectVisibilityPageTest {
+
+  @ClassRule
+  public static Orchestrator orchestrator = ProjectSuite.ORCHESTRATOR;
+
+  @Rule
+  public Tester tester = new Tester(orchestrator);
+
+  private String adminUser;
+
+  @Before
+  public void setUp() {
+    adminUser = tester.users().generateAdministratorOnDefaultOrganization().getLogin();
+  }
+
+  @Test
+  public void return_all_projects_even_when_no_permission() throws Exception {
+    orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-sample")).setProperties("sonar.projectKey", "sample1"));
+    orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-sample")).setProperties("sonar.projectKey", "sample2"));
+    tester.wsClient().projects().updateVisibility(UpdateVisibilityRequest.builder().setProject("sample2").setVisibility("private").build());
+    // Remove 'Admin' permission for admin group on project 2 -> No one can access or admin this project, expect System Admin
+    tester.wsClient().permissions().removeGroup(new RemoveGroupWsRequest().setProjectKey("sample2").setGroupName("sonar-administrators").setPermission("admin"));
+
+    tester.openBrowser().logIn().submitCredentials(adminUser)
+      .openProjectsManagement("default-organization")
+      .shouldHaveProject("sample1")
+      .shouldHaveProject("sample2");
+  }
+
+  @Test
+  public void create_public_project() {
+    createProjectAndVerify("public");
+  }
+
+  @Test
+  public void create_private_project() {
+    createProjectAndVerify("private");
+  }
+
+  private void createProjectAndVerify(String visibility) {
+    ProjectsManagementPage page = tester.openBrowser().logIn()
+      .submitCredentials(adminUser, adminUser)
+      .openProjectsManagement("default-organization");
+    page
+      .shouldHaveProjectsCount(0)
+      .createProject("foo", "foo", visibility)
+      .shouldHaveProjectsCount(1);
+
+    WsComponents.SearchProjectsWsResponse response = newAdminWsClient(orchestrator).components().searchProjects(
+      SearchProjectsRequest.builder().build());
+    assertThat(response.getComponentsCount()).isEqualTo(1);
+    assertThat(response.getComponents(0).getKey()).isEqualTo("foo");
+    assertThat(response.getComponents(0).getName()).isEqualTo("foo");
+    assertThat(response.getComponents(0).getVisibility()).isEqualTo(visibility);
+  }
+
+}
diff --git a/tests/src/test/java/org/sonarqube/tests/project/ProjectsPageTest.java b/tests/src/test/java/org/sonarqube/tests/project/ProjectsPageTest.java
new file mode 100644 (file)
index 0000000..d0008cc
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * 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.tests.project;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.RuleChain;
+import org.sonarqube.qa.util.Tester;
+import org.sonarqube.qa.util.pageobjects.Navigation;
+import org.sonarqube.qa.util.pageobjects.projects.ProjectsPage;
+import org.sonarqube.ws.WsUsers;
+import org.sonarqube.ws.client.PostRequest;
+import org.sonarqube.ws.client.WsClient;
+
+import static com.codeborne.selenide.Selenide.clearBrowserLocalStorage;
+import static com.codeborne.selenide.WebDriverRunner.url;
+import static org.assertj.core.api.Assertions.assertThat;
+import static util.ItUtils.projectDir;
+
+public class ProjectsPageTest {
+
+  private static final String PROJECT_KEY = "key-foo";
+
+  @ClassRule
+  public static Orchestrator orchestrator = ProjectSuite.ORCHESTRATOR;
+
+  private static Tester tester = new Tester(orchestrator);
+
+  @ClassRule
+  public static RuleChain ruleChain = RuleChain.outerRule(orchestrator).around(tester);
+
+  @BeforeClass
+  public static void setUp() {
+    orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-sample")).setProjectKey(PROJECT_KEY));
+    orchestrator.executeBuild(SonarScanner.create(projectDir("duplications/file-duplications")).setProjectKey("key-bar"));
+  }
+
+  @Before
+  public void before() {
+    clearBrowserLocalStorage();
+  }
+
+  @Test
+  public void should_display_projects() {
+    ProjectsPage page = tester.openBrowser().openProjects();
+    page.shouldHaveTotal(2);
+    page.getProjectByKey(PROJECT_KEY)
+      .shouldHaveMeasure("reliability_rating", "A")
+      .shouldHaveMeasure("security_rating", "A")
+      .shouldHaveMeasure("sqale_rating", "A")
+      .shouldHaveMeasure("duplicated_lines_density", "0.0%")
+      .shouldHaveMeasure("ncloc", "13")
+      .shouldHaveMeasure("ncloc", "Xoo");
+  }
+
+  @Test
+  public void should_display_facets() {
+    ProjectsPage page = tester.openBrowser().openProjects();
+    page.getFacetByProperty("duplications")
+      .shouldHaveValue("1", "1")
+      .shouldHaveValue("2", "1")
+      .shouldHaveValue("3", "1")
+      .shouldHaveValue("4", "1")
+      .shouldHaveValue("5", "1")
+      .shouldHaveValue("6", "0");
+  }
+
+  @Test
+  public void should_filter_using_facet() {
+    ProjectsPage page = tester.openBrowser().openProjects();
+    page.shouldHaveTotal(2);
+    page.getFacetByProperty("duplications").selectValue("3");
+    page.shouldHaveTotal(1);
+  }
+
+  @Test
+  public void should_open_default_page() {
+    // default page can be "All Projects" or "Favorite Projects" depending on your last choice
+    Navigation nav = tester.openBrowser();
+    ProjectsPage page = nav.openProjects();
+
+    // all projects for anonymous user with default sorting to analysis date
+    page.shouldHaveTotal(2).shouldDisplayAllProjectsWidthSort("-analysis_date");
+
+    // all projects by default for logged in user
+    WsUsers.CreateWsResponse.User administrator = tester.users().generateAdministratorOnDefaultOrganization();
+    page = nav.logIn().submitCredentials(administrator.getLogin()).openProjects();
+    page.shouldHaveTotal(2).shouldDisplayAllProjects();
+
+    // favorite one project
+    WsClient administratorWsClient = tester.as(administrator.getLogin()).wsClient();
+    administratorWsClient.favorites().add(PROJECT_KEY);
+    page = nav.openProjects();
+    page.shouldHaveTotal(1).shouldDisplayFavoriteProjects();
+
+    // un-favorite this project
+    administratorWsClient.favorites().remove(PROJECT_KEY);
+    page = nav.openProjects();
+    page.shouldHaveTotal(2).shouldDisplayAllProjects();
+
+    // select favorite
+    page.selectFavoriteProjects();
+    page = nav.openProjects();
+    page.shouldHaveTotal(0).shouldDisplayFavoriteProjects();
+
+    // select all
+    page.selectAllProjects();
+    page = nav.openProjects();
+    page.shouldHaveTotal(2).shouldDisplayAllProjects();
+  }
+
+  @Test
+  public void should_add_language_to_facet() {
+    ProjectsPage page = tester.openBrowser().openProjects();
+    page.getFacetByProperty("languages")
+      .selectOptionItem("xoo2")
+      .shouldHaveValue("xoo2", "0");
+  }
+
+  @Test
+  public void should_add_tag_to_facet() {
+    // Add some tags to this project
+    tester.wsClient().wsConnector().call(
+      new PostRequest("api/project_tags/set")
+        .setParam("project", PROJECT_KEY)
+        .setParam("tags", "aa,bb,cc,dd,ee,ff,gg,hh,ii,jj,zz"));
+
+    ProjectsPage page = tester.openBrowser().openProjects();
+    page.getFacetByProperty("tags")
+      .shouldHaveValue("aa", "1")
+      .shouldHaveValue("ii", "1")
+      .selectOptionItem("zz")
+      .shouldHaveValue("zz", "1");
+  }
+
+  @Test
+  public void should_switch_between_perspectives() {
+    WsUsers.CreateWsResponse.User administrator = tester.users().generateAdministratorOnDefaultOrganization();
+    ProjectsPage page = tester.openBrowser()
+      .logIn().submitCredentials(administrator.getLogin())
+      .openProjects();
+    page.changePerspective("Risk");
+    assertThat(url()).endsWith("/projects?view=visualizations&visualization=risk");
+    page.changePerspective("Leak");
+    assertThat(url()).endsWith("/projects?view=leak");
+  }
+
+  @Test
+  public void should_sort_by_facet() {
+    ProjectsPage page = tester.openBrowser().openProjects();
+    page.sortProjects("Duplications");
+    page.getProjectByIdx(0).shouldHaveMeasure("duplicated_lines_density", "63.7%");
+    page.invertSorting();
+    page.getProjectByIdx(0).shouldHaveMeasure("duplicated_lines_density", "0.0%");
+  }
+
+  @Test
+  public void should_search_for_project() {
+    ProjectsPage page = tester.openBrowser().openProjects();
+    page.searchProject("s").shouldHaveTotal(2);
+    page.searchProject("sam").shouldHaveTotal(1);
+  }
+
+  @Test
+  public void should_search_for_project_and_keep_other_filters() {
+    ProjectsPage page = tester.openBrowser().openProjects();
+    page.shouldHaveTotal(2);
+    page.getFacetByProperty("duplications").selectValue("3");
+    page.shouldHaveTotal(1);
+    page.searchProject("sample").shouldHaveTotal(0);
+  }
+
+  @Test
+  public void should_open_permalink() {
+    String user = tester.users().generate().getLogin();
+    Navigation nav = tester.openBrowser().logIn().submitCredentials(user);
+
+    // make a search, so its parameters saved to local storage
+    nav.openProjects().changePerspective("Leak");
+
+    // change a page
+    nav.openHome();
+
+    // open a permalink to a particular visualization, it must be kept
+    nav.openProjectsWithQuery("view=visualizations&visualization=coverage");
+    assertThat(url()).contains("view=visualizations&visualization=coverage");
+  }
+}
diff --git a/tests/src/test/java/org/sonarqube/tests/projectAdministration/BackgroundTasksTest.java b/tests/src/test/java/org/sonarqube/tests/projectAdministration/BackgroundTasksTest.java
deleted file mode 100644 (file)
index 0e7eee2..0000000
+++ /dev/null
@@ -1,115 +0,0 @@
-/*
- * 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.tests.projectAdministration;
-
-import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.build.SonarScanner;
-import org.sonarqube.tests.Category1Suite;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonarqube.qa.util.pageobjects.BackgroundTaskItem;
-import org.sonarqube.qa.util.pageobjects.BackgroundTasksPage;
-import org.sonarqube.qa.util.pageobjects.Navigation;
-import util.user.UserRule;
-
-import static com.codeborne.selenide.CollectionCondition.sizeGreaterThan;
-import static util.ItUtils.projectDir;
-import static util.selenium.Selenese.runSelenese;
-
-public class BackgroundTasksTest {
-
-  private static final String ADMIN_USER_LOGIN = "admin-user";
-
-  @ClassRule
-  public static Orchestrator ORCHESTRATOR = Category1Suite.ORCHESTRATOR;
-
-  @Rule
-  public UserRule userRule = UserRule.from(ORCHESTRATOR);
-
-  private Navigation nav = Navigation.create(ORCHESTRATOR);
-
-  @BeforeClass
-  public static void beforeClass() {
-    executeBuild("test-project", "Test Project");
-    executeBuild("test-project-2", "Another Test Project");
-  }
-
-  @Before
-  public void before() {
-    userRule.createAdminUser(ADMIN_USER_LOGIN, ADMIN_USER_LOGIN);
-  }
-
-  @After
-  public void deleteAdminUser() {
-    userRule.resetUsers();
-  }
-
-  @Test
-  public void should_not_display_failing_and_search_and_filter_elements_on_project_level_page() throws Exception {
-    runSelenese(ORCHESTRATOR, "/projectAdministration/BackgroundTasksTest/should_not_display_failing_and_search_and_filter_elements_on_project_level_page.html");
-  }
-
-  @Test
-  public void display_scanner_context() {
-    nav.logIn().submitCredentials(ADMIN_USER_LOGIN);
-    BackgroundTasksPage page = nav.openBackgroundTasksPage();
-
-    page.getTasks().shouldHave(sizeGreaterThan(0));
-    BackgroundTaskItem task = page.getTasksAsItems().get(0);
-    task.openActions()
-      .openScannerContext()
-      .assertScannerContextContains("SonarQube plugins:")
-      .assertScannerContextContains("Global properties:");
-  }
-
-  @Test
-  public void display_error_stacktrace() {
-    Navigation nav = Navigation.create(ORCHESTRATOR);
-    executeBuild("test-project", "Test Project", "2010-01-01");
-
-    nav.logIn().submitCredentials(ADMIN_USER_LOGIN);
-    BackgroundTasksPage page = nav.openBackgroundTasksPage();
-
-    page.getTasks().shouldHave(sizeGreaterThan(0));
-    BackgroundTaskItem task = page.getTasksAsItems().get(0);
-    task.openActions()
-      .openErrorStacktrace()
-      .assertErrorStacktraceContains("Date of analysis cannot be older than the date of the last known analysis");
-  }
-
-  private static void executeBuild(String projectKey, String projectName) {
-    ORCHESTRATOR.executeBuild(
-      SonarScanner.create(projectDir("shared/xoo-sample"))
-        .setProjectKey(projectKey)
-        .setProjectName(projectName));
-  }
-
-  private static void executeBuild(String projectKey, String projectName, String date) {
-    ORCHESTRATOR.executeBuild(
-      SonarScanner.create(projectDir("shared/xoo-sample"))
-        .setProjectKey(projectKey)
-        .setProjectName(projectName)
-        .setProperty("sonar.projectDate", date));
-  }
-}
index b97d71f9e468b0a4a99ef3663df3aa9ab1a7595f..92fac69dda2355b0568d123570fe734195486687 100644 (file)
@@ -23,18 +23,13 @@ import com.sonar.orchestrator.Orchestrator;
 import com.sonar.orchestrator.build.SonarScanner;
 import java.io.UnsupportedEncodingException;
 import java.sql.SQLException;
-import java.util.Date;
 import javax.annotation.Nullable;
-import org.apache.commons.lang.time.DateFormatUtils;
 import org.junit.Before;
 import org.junit.ClassRule;
 import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.openqa.selenium.By;
-import org.sonar.wsclient.SonarClient;
-import org.sonar.wsclient.base.HttpException;
-import org.sonar.wsclient.user.UserParameters;
 import org.sonarqube.qa.util.Tester;
 import org.sonarqube.qa.util.pageobjects.Navigation;
 import org.sonarqube.qa.util.pageobjects.ProjectsManagementPage;
@@ -44,21 +39,12 @@ import org.sonarqube.ws.WsPermissions;
 import org.sonarqube.ws.client.permission.AddUserToTemplateWsRequest;
 import org.sonarqube.ws.client.permission.CreateTemplateWsRequest;
 import org.sonarqube.ws.client.permission.UsersWsRequest;
-import org.sonarqube.ws.client.project.SearchWsRequest;
 
 import static com.codeborne.selenide.Selenide.$;
-import static java.util.Collections.singletonList;
-import static org.apache.commons.lang.time.DateUtils.addDays;
 import static org.assertj.core.api.Assertions.assertThat;
-import static util.ItUtils.getComponent;
 import static util.ItUtils.projectDir;
-import static util.selenium.Selenese.runSelenese;
 
 public class ProjectAdministrationTest {
-  private static final String DELETE_WS_ENDPOINT = "api/projects/bulk_delete";
-
-  // take some day in the past
-  private static final String ANALYSIS_DATE = DateFormatUtils.ISO_DATE_FORMAT.format(addDays(new Date(), -1));
 
   @ClassRule
   public static Orchestrator orchestrator = Category1Suite.ORCHESTRATOR;
@@ -71,8 +57,6 @@ public class ProjectAdministrationTest {
 
   private Navigation nav = Navigation.create(orchestrator);
 
-  private static final String PROJECT_KEY = "sample";
-  private static final String FILE_KEY = "sample:src/main/xoo/sample/Sample.xoo";
   private String adminUser;
 
   @Before
@@ -81,69 +65,6 @@ public class ProjectAdministrationTest {
     adminUser = tester.users().generateAdministrator().getLogin();
   }
 
-  @Test
-  public void delete_project_by_web_service() {
-    scanSampleWithDate(ANALYSIS_DATE);
-
-    assertThat(getComponent(orchestrator, PROJECT_KEY)).isNotNull();
-    assertThat(getComponent(orchestrator, FILE_KEY)).isNotNull();
-
-    orchestrator.getServer().adminWsClient().post(DELETE_WS_ENDPOINT, "keys", PROJECT_KEY);
-
-    assertThat(getComponent(orchestrator, PROJECT_KEY)).isNull();
-    assertThat(getComponent(orchestrator, FILE_KEY)).isNull();
-  }
-
-  @Test
-  public void fail_when_trying_to_delete_a_file() {
-    scanSampleWithDate(ANALYSIS_DATE);
-    assertThat(getComponent(orchestrator, PROJECT_KEY)).isNotNull();
-    assertThat(getComponent(orchestrator, FILE_KEY)).isNotNull();
-
-    expectedException.expect(org.sonarqube.ws.client.HttpException.class);
-
-    tester.wsClient().projects().bulkDelete(SearchWsRequest.builder()
-      .setQualifiers(singletonList("FIL"))
-      .setProjects(singletonList(FILE_KEY)).build());
-  }
-
-  @Test
-  public void fail_when_insufficient_privilege() {
-    expectedException.expect(HttpException.class);
-    scanSampleWithDate(ANALYSIS_DATE);
-
-    assertThat(getComponent(orchestrator, PROJECT_KEY)).isNotNull();
-
-    // use wsClient() instead of adminWsClient()
-    orchestrator.getServer().wsClient().post(DELETE_WS_ENDPOINT, "keys", PROJECT_KEY);
-  }
-
-  /**
-   * Test updated for SONAR-3570 and SONAR-5923
-   */
-  @Test
-  public void project_deletion() {
-    String projectAdminUser = "project-deletion-with-admin-permission-on-project";
-    SonarClient wsClient = orchestrator.getServer().adminWsClient();
-    try {
-      SonarScanner scan = SonarScanner.create(projectDir("shared/xoo-sample"));
-      orchestrator.executeBuild(scan);
-
-      // Create user having admin permission on previously analysed project
-      wsClient.userClient().create(
-        UserParameters.create().login(projectAdminUser).name(projectAdminUser).password("password").passwordConfirmation("password"));
-
-      wsClient.post("api/permissions/add_user",
-        "login", projectAdminUser,
-        "projectKey", "sample",
-        "permission", "admin");
-
-      runSelenese(orchestrator, "/projectAdministration/ProjectAdministrationTest/project-deletion/project-deletion.html");
-    } finally {
-      wsClient.userClient().deactivate(projectAdminUser);
-    }
-  }
-
   @Test
   public void display_project_settings() throws UnsupportedEncodingException {
     scanSample(null, null);
@@ -214,10 +135,6 @@ public class ProjectAdministrationTest {
     assertThat(usersResponse.getUsers(0).getLogin()).isEqualTo(user);
   }
 
-  private void scanSampleWithDate(String date) {
-    scanSample(date, null);
-  }
-
   private void scanSample(@Nullable String date, @Nullable String profile) {
     SonarScanner scan = SonarScanner.create(projectDir("shared/xoo-sample"))
       .setProperty("sonar.cpd.exclusions", "**/*");
@@ -229,8 +146,4 @@ public class ProjectAdministrationTest {
     }
     orchestrator.executeBuild(scan);
   }
-
-  private int count(String condition) {
-    return orchestrator.getDatabase().countSql("select count(1) from " + condition);
-  }
 }
diff --git a/tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectBulkDeleteTest.java b/tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectBulkDeleteTest.java
deleted file mode 100644 (file)
index 71ac12d..0000000
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * 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.tests.projectAdministration;
-
-import com.sonar.orchestrator.Orchestrator;
-import java.util.List;
-import java.util.stream.IntStream;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonarqube.tests.Category6Suite;
-import org.sonarqube.qa.util.Tester;
-import org.sonarqube.ws.Organizations;
-import org.sonarqube.ws.WsProjects.CreateWsResponse;
-import org.sonarqube.ws.WsProjects.SearchWsResponse.Component;
-import org.sonarqube.ws.client.project.SearchWsRequest;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static util.ItUtils.runProjectAnalysis;
-
-public class ProjectBulkDeleteTest {
-
-  @ClassRule
-  public static Orchestrator orchestrator = Category6Suite.ORCHESTRATOR;
-  @Rule
-  public Tester tester = new Tester(orchestrator);
-
-  @Test
-  public void delete_projects() {
-    Organizations.Organization organization = tester.organizations().generate();
-    CreateWsResponse.Project firstProvisionedProject = tester.projects().generate(organization, p -> p.setKey("first-provisioned-project"));
-    CreateWsResponse.Project secondProvisionedProject = tester.projects().generate(organization, p -> p.setKey("second-provisioned-project"));
-    CreateWsResponse.Project analyzedProject = tester.projects().generate(organization);
-
-    analyzeProject(analyzedProject.getKey(), organization.getKey());
-
-    tester.wsClient().projects().bulkDelete(SearchWsRequest.builder()
-      .setOrganization(organization.getKey())
-      .setQuery("FIRST-PROVISIONED")
-      .setOnProvisionedOnly(true).build());
-
-    List<Component> projects = tester.wsClient().projects().search(SearchWsRequest.builder().setOrganization(organization.getKey()).build()).getComponentsList();
-    assertThat(projects).extracting(Component::getKey)
-      .containsExactlyInAnyOrder(analyzedProject.getKey(), secondProvisionedProject.getKey())
-      .doesNotContain(firstProvisionedProject.getKey());
-  }
-
-  @Test
-  public void delete_more_than_50_projects_at_the_same_time() {
-    Organizations.Organization organization = tester.organizations().generate();
-    IntStream.range(0, 60).forEach(i -> tester.projects().generate(organization));
-    SearchWsRequest request = SearchWsRequest.builder().setOrganization(organization.getKey()).build();
-    assertThat(tester.wsClient().projects().search(request).getPaging().getTotal()).isEqualTo(60);
-
-    tester.wsClient().projects().bulkDelete(request);
-
-    assertThat(tester.wsClient().projects().search(request).getComponentsList()).isEmpty();
-    assertThat(tester.wsClient().projects().search(request).getPaging().getTotal()).isEqualTo(0);
-  }
-
-  private void analyzeProject(String projectKey, String organizationKey) {
-    runProjectAnalysis(orchestrator, "shared/xoo-sample",
-      "sonar.organization", organizationKey,
-      "sonar.projectKey", projectKey,
-      "sonar.login", "admin",
-      "sonar.password", "admin");
-  }
-}
diff --git a/tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectBulkDeletionPageTest.java b/tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectBulkDeletionPageTest.java
deleted file mode 100644 (file)
index 988f377..0000000
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * 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.tests.projectAdministration;
-
-import com.sonar.orchestrator.Orchestrator;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonarqube.tests.Category1Suite;
-import org.sonarqube.qa.util.Tester;
-import org.sonarqube.ws.WsProjects.CreateWsResponse.Project;
-import org.sonarqube.ws.client.component.SearchProjectsRequest;
-
-import static com.codeborne.selenide.Condition.text;
-import static com.codeborne.selenide.Condition.visible;
-import static com.codeborne.selenide.Selenide.$;
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class ProjectBulkDeletionPageTest {
-
-  private String adminUser;
-
-  @ClassRule
-  public static Orchestrator orchestrator = Category1Suite.ORCHESTRATOR;
-
-  @Rule
-  public Tester tester = new Tester(orchestrator);
-
-  @Before
-  public void deleteData() {
-    orchestrator.resetData();
-    adminUser = tester.users().generateAdministrator().getLogin();
-  }
-
-  /**
-   * SONAR-2614, SONAR-3805
-   */
-  @Test
-  public void test_bulk_deletion_on_selected_projects() throws Exception {
-    Project project1 = tester.projects().generate(null, t -> t.setName("Foo"));
-    Project project2 = tester.projects().generate(null, t -> t.setName("Bar"));
-    Project project3 = tester.projects().generate(null, t -> t.setName("FooQux"));
-
-    tester.openBrowser().logIn().submitCredentials(adminUser).open("/admin/projects_management");
-    $("#projects-management-page").shouldHave(text(project1.getName())).shouldHave(text(project2.getName())).shouldHave(text(project3.getName()));
-
-    $("#projects-management-page .search-box-input").val("foo").pressEnter();
-    $("#projects-management-page").shouldNotHave(text(project2.getName())).shouldHave(text(project1.getName())).shouldHave(text(project3.getName()));
-
-    $("#projects-management-page .js-delete").click();
-    $(".modal").shouldBe(visible);
-    $(".modal button").click();
-    $("#projects-management-page").shouldNotHave(text(project1.getName())).shouldNotHave(text(project3.getName()));
-
-    assertThat(tester.wsClient().components().searchProjects(SearchProjectsRequest.builder().build())
-      .getComponentsCount()).isEqualTo(1);
-  }
-}
diff --git a/tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectDeleteTest.java b/tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectDeleteTest.java
deleted file mode 100644 (file)
index 788a94d..0000000
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * 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.tests.projectAdministration;
-
-import com.sonar.orchestrator.Orchestrator;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Map;
-import java.util.stream.Collectors;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.DisableOnDebug;
-import org.junit.rules.TestRule;
-import org.junit.rules.Timeout;
-import org.sonarqube.tests.Category6Suite;
-import org.sonarqube.qa.util.Tester;
-import org.sonarqube.ws.Organizations;
-import org.sonarqube.ws.WsComponents;
-import org.sonarqube.ws.WsProjects;
-import org.sonarqube.ws.WsProjects.CreateWsResponse.Project;
-import org.sonarqube.ws.client.GetRequest;
-import org.sonarqube.ws.client.WsResponse;
-import org.sonarqube.ws.client.component.SearchProjectsRequest;
-import org.sonarqube.ws.client.project.CreateRequest;
-import org.sonarqube.ws.client.project.DeleteRequest;
-import org.sonarqube.ws.client.project.SearchWsRequest;
-import util.ItUtils;
-
-import static java.util.Collections.singletonList;
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class ProjectDeleteTest {
-
-  @ClassRule
-  public static final Orchestrator orchestrator = Category6Suite.ORCHESTRATOR;
-
-  @Rule
-  public TestRule safeguard = new DisableOnDebug(Timeout.seconds(300));
-  @Rule
-  public Tester tester = new Tester(orchestrator)
-    .setElasticsearchHttpPort(Category6Suite.SEARCH_HTTP_PORT);
-
-  @Test
-  public void deletion_removes_project_from_search_engines() {
-    Organizations.Organization organization = tester.organizations().generate();
-    Project project1 = createProject(organization, "one", "Foo");
-    Project project2 = createProject(organization, "two", "Bar");
-    assertThatProjectIsSearchable(organization, "Foo");
-    assertThatProjectIsSearchable(organization, "Bar");
-
-    deleteProject(project1);
-    assertThatProjectIsNotSearchable(organization, project1.getName());
-    assertThatProjectIsSearchable(organization, project2.getName());
-
-    deleteProject(project2);
-    assertThatProjectIsNotSearchable(organization, project1.getName());
-    assertThatProjectIsNotSearchable(organization, project2.getName());
-  }
-
-  @Test
-  public void indexing_errors_are_recovered_asynchronously_when_deleting_project() throws Exception {
-    Organizations.Organization organization = tester.organizations().generate();
-    Project project = createProject(organization, "one", "Foo");
-
-    tester.elasticsearch().lockWrites("components");
-    tester.elasticsearch().lockWrites("projectmeasures");
-    deleteProject(project);
-    // WS reloads from database the results returned by Elasticsearch. That's
-    // why the project does not appear in search engine.
-    // However this test is still useful to verify that WS do not
-    // fail during this Elasticsearch inconsistency.
-    assertThatProjectIsNotSearchable(organization, project.getName());
-
-    tester.elasticsearch().unlockWrites("components");
-    tester.elasticsearch().unlockWrites("projectmeasures");
-    // TODO verify that recovery daemon successfully updated indices
-  }
-
-  @Test
-  public void bulk_deletion_removes_projects_from_search_engines() {
-    Organizations.Organization organization = tester.organizations().generate();
-    Project project1 = createProject(organization, "one", "Foo");
-    Project project2 = createProject(organization, "two", "Bar");
-    Project project3 = createProject(organization, "three", "Baz");
-
-    bulkDeleteProjects(organization, project1, project3);
-    assertThatProjectIsNotSearchable(organization, project1.getName());
-    assertThatProjectIsSearchable(organization, project2.getName());
-    assertThatProjectIsNotSearchable(organization, project3.getName());
-  }
-
-  @Test
-  public void indexing_errors_are_recovered_asynchronously_when_bulk_deleting_projects() throws Exception {
-    Organizations.Organization organization = tester.organizations().generate();
-    Project project1 = createProject(organization, "one", "Foo");
-    Project project2 = createProject(organization, "two", "Bar");
-    Project project3 = createProject(organization, "three", "Baz");
-
-    tester.elasticsearch().lockWrites("components");
-    tester.elasticsearch().lockWrites("projectmeasures");
-    bulkDeleteProjects(organization, project1, project3);
-
-    // WS reloads from database the results returned by Elasticsearch. That's
-    // why the project does not appear in search engine.
-    // However this test is still useful to verify that WS do not
-    // fail during this Elasticsearch inconsistency.
-    assertThatProjectIsNotSearchable(organization, project1.getName());
-    assertThatProjectIsSearchable(organization, project2.getName());
-    assertThatProjectIsNotSearchable(organization, project3.getName());
-
-    tester.elasticsearch().unlockWrites("components");
-    tester.elasticsearch().unlockWrites("projectmeasures");
-    // TODO verify that recovery daemon successfully updated indices
-  }
-
-  private void deleteProject(Project project) {
-    tester.wsClient().projects().delete(DeleteRequest.builder().setKey(project.getKey()).build());
-  }
-
-  private void bulkDeleteProjects(Organizations.Organization organization, Project... projects) {
-    SearchWsRequest request = SearchWsRequest.builder()
-      .setOrganization(organization.getKey())
-      .setProjects(Arrays.stream(projects).map(Project::getKey).collect(Collectors.toList()))
-      .build();
-    tester.wsClient().projects().bulkDelete(request);
-  }
-
-  private Project createProject(Organizations.Organization organization, String key, String name) {
-    CreateRequest createRequest = CreateRequest.builder().setKey(key).setName(name).setOrganization(organization.getKey()).build();
-    return tester.wsClient().projects().create(createRequest).getProject();
-  }
-
-  private void assertThatProjectIsSearchable(Organizations.Organization organization, String name) {
-    assertThat(isInProjectsSearch(organization, name)).isTrue();
-    assertThat(isInComponentSearchProjects(name)).isTrue();
-    assertThat(isInComponentSuggestions(name)).isTrue();
-  }
-
-  private void assertThatProjectIsNotSearchable(Organizations.Organization organization, String name) {
-    assertThat(isInProjectsSearch(organization, name)).isFalse();
-    assertThat(isInComponentSearchProjects(name)).isFalse();
-    assertThat(isInComponentSuggestions(name)).isFalse();
-  }
-
-  /**
-   * Projects administration page - uses database
-   */
-  private boolean isInProjectsSearch(Organizations.Organization organization, String name) {
-    WsProjects.SearchWsResponse response = tester.wsClient().projects().search(
-      SearchWsRequest.builder().setOrganization(organization.getKey()).setQuery(name).setQualifiers(singletonList("TRK")).build());
-    return response.getComponentsCount() > 0;
-  }
-
-  /**
-   * Projects page - api/components/search_projects - uses ES + DB
-   */
-  private boolean isInComponentSearchProjects(String name) {
-    WsComponents.SearchProjectsWsResponse response = tester.wsClient().components().searchProjects(
-      SearchProjectsRequest.builder().setFilter("query=\"" + name + "\"").build());
-    return response.getComponentsCount() > 0;
-  }
-
-  /**
-   * Top-right search engine - api/components/suggestions - uses ES + DB
-   */
-  private boolean isInComponentSuggestions(String name) {
-    GetRequest request = new GetRequest("api/components/suggestions").setParam("s", name);
-    WsResponse response = tester.wsClient().wsConnector().call(request);
-    Map<String, Object> json = ItUtils.jsonToMap(response.content());
-    Collection<Map<String, Object>> results = (Collection<Map<String, Object>>) json.get("results");
-    Collection items = results.stream()
-      .filter(map -> "TRK".equals(map.get("q")))
-      .map(map -> (Collection) map.get("items"))
-      .findFirst()
-      .orElseThrow(() -> new IllegalStateException("missing field results/[q=TRK]"));
-    return !items.isEmpty();
-  }
-}
diff --git a/tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectKeyUpdateTest.java b/tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectKeyUpdateTest.java
deleted file mode 100644 (file)
index f1e1ba4..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-/*
- * 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.tests.projectAdministration;
-
-import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.build.SonarScanner;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-import javax.annotation.CheckForNull;
-import org.junit.After;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.DisableOnDebug;
-import org.junit.rules.TestRule;
-import org.junit.rules.Timeout;
-import org.sonarqube.tests.Category6Suite;
-import org.sonarqube.qa.util.Tester;
-import org.sonarqube.ws.Organizations;
-import org.sonarqube.ws.WsComponents;
-import org.sonarqube.ws.WsProjects;
-import org.sonarqube.ws.client.GetRequest;
-import org.sonarqube.ws.client.WsResponse;
-import org.sonarqube.ws.client.component.SearchProjectsRequest;
-import org.sonarqube.ws.client.project.CreateRequest;
-import org.sonarqube.ws.client.project.UpdateKeyWsRequest;
-import util.ItUtils;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static util.ItUtils.projectDir;
-
-public class ProjectKeyUpdateTest {
-
-  @ClassRule
-  public static final Orchestrator orchestrator = Category6Suite.ORCHESTRATOR;
-
-  @Rule
-  public TestRule safeguard = new DisableOnDebug(Timeout.seconds(300));
-  @Rule
-  public Tester tester = new Tester(orchestrator)
-    .setElasticsearchHttpPort(Category6Suite.SEARCH_HTTP_PORT);
-
-  @After
-  public void tearDown() throws Exception {
-    unlockWritesOnProjectIndices();
-  }
-
-  @Test
-  public void update_key_of_provisioned_project() {
-    Organizations.Organization organization = tester.organizations().generate();
-    WsProjects.CreateWsResponse.Project project = createProject(organization, "one", "Foo");
-
-    updateKey(project, "two");
-
-    assertThat(isProjectInDatabase("one")).isFalse();
-    assertThat(isProjectInDatabase("two")).isTrue();
-    assertThat(isComponentInDatabase("one")).isFalse();
-    assertThat(isComponentInDatabase("two")).isTrue();
-    assertThat(keyInComponentSearchProjects("Foo")).isEqualTo("two");
-    assertThat(keysInComponentSuggestions("Foo")).containsExactly("two");
-  }
-
-  @Test
-  public void recover_indexing_errors_when_updating_key_of_provisioned_project() throws Exception {
-    Organizations.Organization organization = tester.organizations().generate();
-    WsProjects.CreateWsResponse.Project project = createProject(organization, "one", "Foo");
-
-    lockWritesOnProjectIndices();
-
-    updateKey(project, "two");
-
-    assertThat(isProjectInDatabase("one")).isFalse();
-
-    // WS gets the list of projects from ES then reloads projects from db.
-    // That's why keys in WS responses are correct.
-    assertThat(isProjectInDatabase("one")).isFalse();
-    assertThat(isProjectInDatabase("two")).isTrue();
-    assertThat(keyInComponentSearchProjects("Foo")).isEqualTo("two");
-    assertThat(keysInComponentSuggestions("Foo")).containsExactly("two");
-
-    // however searching by key is inconsistent
-    assertThat(keyInComponentSearchProjects("one")).isEqualTo("two");
-    assertThat(keysInComponentSuggestions("one")).containsExactly("two");
-    assertThat(keyInComponentSearchProjects("two")).isNull();
-    assertThat(keysInComponentSuggestions("two")).isEmpty();
-
-    unlockWritesOnProjectIndices();
-
-    boolean recovered = false;
-    while (!recovered) {
-      // recovery daemon runs every second, see Category6Suite
-      Thread.sleep(1_000L);
-      recovered = keyInComponentSearchProjects("one") == null &&
-        keysInComponentSuggestions("one").isEmpty() &&
-        "two".equals(keyInComponentSearchProjects("two")) &&
-        keysInComponentSuggestions("two").contains("two");
-    }
-  }
-
-  @Test
-  public void update_key_of_module() {
-    Organizations.Organization organization = tester.organizations().generate();
-    orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-multi-modules-sample"),
-      "sonar.organization", organization.getKey(),
-      "sonar.login", "admin", "sonar.password", "admin"));
-
-    String initialKey = "com.sonarsource.it.samples:multi-modules-sample:module_a";
-    String newKey = "com.sonarsource.it.samples:multi-modules-sample:module_c";
-
-    updateKey(initialKey, newKey);
-
-    assertThat(isComponentInDatabase(initialKey)).isFalse();
-    assertThat(isComponentInDatabase(newKey)).isTrue();
-    // suggestions engine ignores one-character words, so we can't search for "Module A"
-    assertThat(keysInComponentSuggestions("Module"))
-      .contains(newKey)
-      .doesNotContain(initialKey);
-    assertThat(keysInComponentSuggestions(newKey))
-      .contains(newKey)
-      .doesNotContain(initialKey);
-    assertThat(keysInComponentSuggestions(initialKey)).isEmpty();
-
-  }
-
-  @Test
-  public void recover_indexing_errors_when_updating_key_of_module() throws Exception {
-    Organizations.Organization organization = tester.organizations().generate();
-    orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-multi-modules-sample"),
-      "sonar.organization", organization.getKey(),
-      "sonar.login", "admin", "sonar.password", "admin"));
-
-    String initialKey = "com.sonarsource.it.samples:multi-modules-sample:module_a";
-    String newKey = "com.sonarsource.it.samples:multi-modules-sample:module_c";
-
-    lockWritesOnProjectIndices();
-    updateKey(initialKey, newKey);
-
-    // api/components/search loads keys from db, so results are consistent
-    assertThat(isComponentInDatabase(initialKey)).isFalse();
-    assertThat(isComponentInDatabase(newKey)).isTrue();
-
-    // key in result of suggestion engine is loaded from db, so results are ok when searching for unchanged name
-    assertThat(keysInComponentSuggestions("Module"))
-      .contains(newKey)
-      .doesNotContain(initialKey);
-
-    // but searching for new key does not work
-    assertThat(keysInComponentSuggestions(newKey)).isEmpty();
-    assertThat(keysInComponentSuggestions(initialKey))
-      .isNotEmpty()
-      .contains(newKey /* the returned key is loaded from db, so it's correct */);
-
-    unlockWritesOnProjectIndices();
-
-    boolean recovered = false;
-    while (!recovered) {
-      // recovery daemon runs every second, see Category6Suite
-      Thread.sleep(1_000L);
-      recovered = keysInComponentSuggestions(newKey).contains(newKey) && keysInComponentSuggestions(initialKey).isEmpty();
-    }
-
-  }
-
-  private void lockWritesOnProjectIndices() throws Exception {
-    tester.elasticsearch().lockWrites("components");
-    tester.elasticsearch().lockWrites("projectmeasures");
-  }
-
-  private void unlockWritesOnProjectIndices() throws Exception {
-    tester.elasticsearch().unlockWrites("components");
-    tester.elasticsearch().unlockWrites("projectmeasures");
-  }
-
-  private void updateKey(WsProjects.CreateWsResponse.Project project, String newKey) {
-    tester.wsClient().projects().updateKey(UpdateKeyWsRequest.builder().setKey(project.getKey()).setNewKey(newKey).build());
-  }
-
-  private void updateKey(String initialKey, String newKey) {
-    tester.wsClient().projects().updateKey(UpdateKeyWsRequest.builder().setKey(initialKey).setNewKey(newKey).build());
-  }
-
-  private WsProjects.CreateWsResponse.Project createProject(Organizations.Organization organization, String key, String name) {
-    CreateRequest createRequest = CreateRequest.builder().setKey(key).setName(name).setOrganization(organization.getKey()).build();
-    return tester.wsClient().projects().create(createRequest).getProject();
-  }
-
-  private boolean isProjectInDatabase(String projectKey) {
-    return orchestrator.getDatabase().countSql(String.format("select count(id) from projects where qualifier='TRK' and kee='%s'", projectKey)) == 1L;
-  }
-
-  private boolean isComponentInDatabase(String componentKey) {
-    return orchestrator.getDatabase().countSql(String.format("select count(id) from projects where kee='%s'", componentKey)) == 1L;
-  }
-
-  /**
-   * Projects page - api/components/search_projects - uses ES + DB
-   */
-  @CheckForNull
-  private String keyInComponentSearchProjects(String name) {
-    WsComponents.SearchProjectsWsResponse response = tester.wsClient().components().searchProjects(
-      SearchProjectsRequest.builder().setFilter("query=\"" + name + "\"").build());
-    if (response.getComponentsCount() > 0) {
-      return response.getComponents(0).getKey();
-    }
-    return null;
-  }
-
-  /**
-   * Top-right search engine - api/components/suggestions - uses ES + DB
-   */
-  private List<String> keysInComponentSuggestions(String name) {
-    GetRequest request = new GetRequest("api/components/suggestions").setParam("s", name);
-    WsResponse response = tester.wsClient().wsConnector().call(request);
-    Map<String, Object> json = ItUtils.jsonToMap(response.content());
-    Collection<Map<String, Object>> results = (Collection<Map<String, Object>>) json.get("results");
-    return results.stream()
-      .filter(map -> "TRK".equals(map.get("q")) || "BRC".equals(map.get("q")))
-      .flatMap(map -> ((Collection<Map<String, Object>>) map.get("items")).stream())
-      .map(map -> (String) map.get("key"))
-      .collect(Collectors.toList());
-  }
-}
diff --git a/tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectLinksPageTest.java b/tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectLinksPageTest.java
deleted file mode 100644 (file)
index b6ad18a..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * 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.tests.projectAdministration;
-
-import com.codeborne.selenide.Condition;
-import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.build.SonarScanner;
-import org.sonarqube.tests.Category1Suite;
-import java.util.List;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonarqube.ws.WsProjectLinks.CreateWsResponse;
-import org.sonarqube.ws.client.WsClient;
-import org.sonarqube.ws.client.projectlinks.CreateWsRequest;
-import org.sonarqube.ws.client.projectlinks.DeleteWsRequest;
-import org.sonarqube.qa.util.pageobjects.Navigation;
-import org.sonarqube.qa.util.pageobjects.ProjectLinkItem;
-import org.sonarqube.qa.util.pageobjects.ProjectLinksPage;
-import util.user.UserRule;
-
-import static com.codeborne.selenide.Condition.hasText;
-import static com.codeborne.selenide.Selenide.$;
-import static util.ItUtils.newAdminWsClient;
-import static util.ItUtils.projectDir;
-
-public class ProjectLinksPageTest {
-
-  @ClassRule
-  public static Orchestrator ORCHESTRATOR = Category1Suite.ORCHESTRATOR;
-
-  private Navigation nav = Navigation.create(ORCHESTRATOR);
-
-  @Rule
-  public UserRule userRule = UserRule.from(ORCHESTRATOR);
-
-  private static WsClient wsClient;
-  private long customLinkId;
-  private String adminUser;
-
-  @BeforeClass
-  public static void setUp() {
-    wsClient = newAdminWsClient(ORCHESTRATOR);
-
-    ORCHESTRATOR.resetData();
-    ORCHESTRATOR.executeBuild(
-      SonarScanner.create(projectDir("shared/xoo-sample"))
-        .setProperty("sonar.links.homepage", "http://example.com"));
-  }
-
-  @Before
-  public void prepare() {
-    customLinkId = Long.parseLong(createCustomLink().getLink().getId());
-    adminUser = userRule.createAdminUser();
-  }
-
-  @After
-  public void clean() {
-    deleteLink(customLinkId);
-  }
-
-  @Test
-  public void should_list_links() {
-    ProjectLinksPage page = openPage();
-
-    page.getLinks().shouldHaveSize(2);
-
-    List<ProjectLinkItem> links = page.getLinksAsItems();
-    ProjectLinkItem homepageLink = links.get(0);
-    ProjectLinkItem customLink = links.get(1);
-
-    homepageLink.getName().should(hasText("Home"));
-    homepageLink.getType().should(hasText("sonar.links.homepage"));
-    homepageLink.getUrl().should(hasText("http://example.com"));
-    homepageLink.getDeleteButton().shouldNot(Condition.present);
-
-    customLink.getName().should(hasText("Custom"));
-    customLink.getType().shouldNot(Condition.present);
-    customLink.getUrl().should(hasText("http://example.org/custom"));
-    customLink.getDeleteButton().shouldBe(Condition.visible);
-  }
-
-  @Test
-  public void should_create_link() {
-    ProjectLinksPage page = openPage();
-
-    page.getLinks().shouldHaveSize(2);
-
-    $("#create-project-link").click();
-    $("#create-link-name").setValue("Test");
-    $("#create-link-url").setValue("http://example.com/test");
-    $("#create-link-confirm").click();
-
-    page.getLinks().shouldHaveSize(3);
-
-    ProjectLinkItem testLink = page.getLinksAsItems().get(2);
-
-    testLink.getName().should(hasText("Test"));
-    testLink.getType().shouldNot(Condition.present);
-    testLink.getUrl().should(hasText("http://example.com/test"));
-    testLink.getDeleteButton().shouldBe(Condition.visible);
-  }
-
-  @Test
-  public void should_delete_link() {
-    ProjectLinksPage page = openPage();
-
-    page.getLinks().shouldHaveSize(2);
-
-    List<ProjectLinkItem> links = page.getLinksAsItems();
-    ProjectLinkItem customLink = links.get(1);
-
-    customLink.getDeleteButton().click();
-    $("#delete-link-confirm").click();
-
-    page.getLinks().shouldHaveSize(1);
-  }
-
-  private CreateWsResponse createCustomLink() {
-    return wsClient.projectLinks().create(new CreateWsRequest()
-      .setProjectKey("sample")
-      .setName("Custom")
-      .setUrl("http://example.org/custom"));
-  }
-
-  private void deleteLink(long id) {
-    try {
-      wsClient.projectLinks().delete(new DeleteWsRequest().setId(id));
-    } catch (Exception e) {
-      // fail silently
-    }
-  }
-
-  private ProjectLinksPage openPage() {
-    nav.logIn().submitCredentials(adminUser, adminUser);
-    return nav.openProjectLinks("sample");
-  }
-}
diff --git a/tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectProvisioningTest.java b/tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectProvisioningTest.java
deleted file mode 100644 (file)
index 4014044..0000000
+++ /dev/null
@@ -1,133 +0,0 @@
-/*
- * 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.tests.projectAdministration;
-
-import com.sonar.orchestrator.Orchestrator;
-import java.util.Collection;
-import java.util.Map;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.DisableOnDebug;
-import org.junit.rules.TestRule;
-import org.junit.rules.Timeout;
-import org.sonarqube.tests.Category6Suite;
-import org.sonarqube.qa.util.Tester;
-import org.sonarqube.ws.Organizations;
-import org.sonarqube.ws.WsComponents;
-import org.sonarqube.ws.WsProjects;
-import org.sonarqube.ws.WsProjects.CreateWsResponse.Project;
-import org.sonarqube.ws.client.GetRequest;
-import org.sonarqube.ws.client.WsResponse;
-import org.sonarqube.ws.client.component.SearchProjectsRequest;
-import org.sonarqube.ws.client.project.CreateRequest;
-import org.sonarqube.ws.client.project.SearchWsRequest;
-import util.ItUtils;
-
-import static java.util.Collections.singletonList;
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class ProjectProvisioningTest {
-
-  @ClassRule
-  public static final Orchestrator orchestrator = Category6Suite.ORCHESTRATOR;
-
-  @Rule
-  public TestRule safeguard = new DisableOnDebug(Timeout.seconds(300));
-  @Rule
-  public Tester tester = new Tester(orchestrator)
-    .setElasticsearchHttpPort(Category6Suite.SEARCH_HTTP_PORT);
-
-  @Test
-  public void provisioned_project_is_available_in_search_engines() {
-    Organizations.Organization organization = tester.organizations().generate();
-
-    createProject(organization, "one", "Foo");
-
-    assertThat(isInProjectsSearch(organization, "Foo")).isTrue();
-    assertThat(isInComponentSearchProjects("Foo")).isTrue();
-    assertThat(isInComponentSuggestions("Foo")).isTrue();
-  }
-
-  @Test
-  public void indexing_errors_are_recovered_asynchronously_when_provisioning_project() throws Exception {
-    tester.elasticsearch().lockWrites("components");
-    tester.elasticsearch().lockWrites("projectmeasures");
-
-    Organizations.Organization organization = tester.organizations().generate();
-    Project project = createProject(organization, "one", "Foo");
-
-    // no ES requests but only DB
-    assertThat(isInProjectsSearch(organization, project.getName())).isTrue();
-
-    // these WS use ES so they are temporarily inconsistent
-    assertThat(isInComponentSearchProjects(project.getName())).isFalse();
-    assertThat(isInComponentSuggestions(project.getName())).isFalse();
-
-    tester.elasticsearch().unlockWrites("components");
-    tester.elasticsearch().unlockWrites("projectmeasures");
-
-    boolean found = false;
-    while (!found) {
-      // recovery daemon runs every second (see Category6Suite)
-      Thread.sleep(1_000L);
-      found = isInComponentSearchProjects(project.getName()) && isInComponentSuggestions(project.getName());
-    }
-  }
-
-  private Project createProject(Organizations.Organization organization, String key, String name) {
-    CreateRequest createRequest = CreateRequest.builder().setKey(key).setName(name).setOrganization(organization.getKey()).build();
-    return tester.wsClient().projects().create(createRequest).getProject();
-  }
-
-  /**
-   * Projects administration page - uses database
-   */
-  private boolean isInProjectsSearch(Organizations.Organization organization, String name) {
-    WsProjects.SearchWsResponse response = tester.wsClient().projects().search(
-      SearchWsRequest.builder().setOrganization(organization.getKey()).setQuery(name).setQualifiers(singletonList("TRK")).build());
-    return response.getComponentsCount() > 0;
-  }
-
-  /**
-   * Projects page - api/components/search_projects - uses ES + DB
-   */
-  private boolean isInComponentSearchProjects(String name) {
-    WsComponents.SearchProjectsWsResponse response = tester.wsClient().components().searchProjects(
-      SearchProjectsRequest.builder().setFilter("query=\"" + name + "\"").build());
-    return response.getComponentsCount() > 0;
-  }
-
-  /**
-   * Top-right search engine - api/components/suggestions - uses ES + DB
-   */
-  private boolean isInComponentSuggestions(String name) {
-    GetRequest request = new GetRequest("api/components/suggestions").setParam("s", name);
-    WsResponse response = tester.wsClient().wsConnector().call(request);
-    Map<String, Object> json = ItUtils.jsonToMap(response.content());
-    Collection<Map<String, Object>> results = (Collection<Map<String, Object>>) json.get("results");
-    Collection items = results.stream()
-      .filter(map -> "TRK".equals(map.get("q")))
-      .map(map -> (Collection) map.get("items"))
-      .findFirst()
-      .orElseThrow(() -> new IllegalStateException("missing field results/[q=TRK]"));
-    return !items.isEmpty();
-  }
-}
diff --git a/tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectQualityGatePageTest.java b/tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectQualityGatePageTest.java
deleted file mode 100644 (file)
index 43d726a..0000000
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * 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.tests.projectAdministration;
-
-import com.codeborne.selenide.Condition;
-import com.codeborne.selenide.Selenide;
-import com.codeborne.selenide.SelenideElement;
-import com.sonar.orchestrator.Orchestrator;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.ClassRule;
-import org.junit.Test;
-import org.openqa.selenium.Keys;
-import org.sonar.wsclient.qualitygate.QualityGate;
-import org.sonar.wsclient.qualitygate.QualityGateClient;
-import org.sonarqube.qa.util.pageobjects.Navigation;
-import org.sonarqube.qa.util.pageobjects.ProjectQualityGatePage;
-import org.sonarqube.tests.Category1Suite;
-import org.sonarqube.ws.client.PostRequest;
-import org.sonarqube.ws.client.WsClient;
-import org.sonarqube.ws.client.qualitygate.SelectWsRequest;
-
-import static util.ItUtils.newAdminWsClient;
-
-public class ProjectQualityGatePageTest {
-
-  @ClassRule
-  public static Orchestrator ORCHESTRATOR = Category1Suite.ORCHESTRATOR;
-
-  private Navigation nav = Navigation.create(ORCHESTRATOR);
-
-  private static WsClient wsClient;
-
-  @BeforeClass
-  public static void prepare() {
-    wsClient = newAdminWsClient(ORCHESTRATOR);
-  }
-
-  @Before
-  public void setUp() {
-    ORCHESTRATOR.resetData();
-
-    wsClient.wsConnector().call(new PostRequest("api/projects/create")
-      .setParam("name", "Sample")
-      .setParam("key", "sample"));
-  }
-
-  @Test
-  public void should_display_default() {
-    QualityGate customQualityGate = createCustomQualityGate("should_display_default");
-    qualityGateClient().setDefault(customQualityGate.id());
-
-    try {
-      ProjectQualityGatePage page = openPage();
-      SelenideElement selectedQualityGate = page.getSelectedQualityGate();
-      selectedQualityGate.should(Condition.text("Default"));
-      selectedQualityGate.should(Condition.text(customQualityGate.name()));
-    } finally {
-      qualityGateClient().unsetDefault();
-      qualityGateClient().destroy(customQualityGate.id());
-    }
-  }
-
-  @Test
-  public void should_display_custom() {
-    QualityGate customQualityGate = createCustomQualityGate("should_display_custom");
-    associateWithQualityGate(customQualityGate);
-
-    ProjectQualityGatePage page = openPage();
-    SelenideElement selectedQualityGate = page.getSelectedQualityGate();
-    selectedQualityGate.shouldNot(Condition.text("Default"));
-    selectedQualityGate.should(Condition.text(customQualityGate.name()));
-  }
-
-  @Test
-  public void should_display_none() {
-    qualityGateClient().unsetDefault();
-
-    ProjectQualityGatePage page = openPage();
-    page.assertNotSelected();
-  }
-
-  @Test
-  public void should_set_custom() {
-    QualityGate customQualityGate = createCustomQualityGate("should_set_custom");
-
-    ProjectQualityGatePage page = openPage();
-    page.setQualityGate(customQualityGate.name());
-
-    SelenideElement selectedQualityGate = page.getSelectedQualityGate();
-    selectedQualityGate.should(Condition.text(customQualityGate.name()));
-  }
-
-  @Test
-  public void should_set_default() {
-    QualityGate customQualityGate = createCustomQualityGate("should_set_default");
-    qualityGateClient().setDefault(customQualityGate.id());
-
-    try {
-      ProjectQualityGatePage page = openPage();
-      page.setQualityGate(customQualityGate.name());
-
-      SelenideElement selectedQualityGate = page.getSelectedQualityGate();
-      selectedQualityGate.should(Condition.text("Default"));
-      selectedQualityGate.should(Condition.text(customQualityGate.name()));
-    } finally {
-      qualityGateClient().unsetDefault();
-      qualityGateClient().destroy(customQualityGate.id());
-    }
-  }
-
-  @Test
-  public void should_set_none() {
-    qualityGateClient().unsetDefault();
-    QualityGate customQualityGate = createCustomQualityGate("should_set_none");
-    associateWithQualityGate(customQualityGate);
-
-    ProjectQualityGatePage page = openPage();
-    Selenide.$(".Select-input input").sendKeys(Keys.UP, Keys.UP, Keys.UP, Keys.ENTER);
-
-    page.assertNotSelected();
-  }
-
-  private ProjectQualityGatePage openPage() {
-    nav.logIn().submitCredentials("admin", "admin");
-    Selenide.$(".js-skip.text-muted").pressEscape();
-    return nav.openProjectQualityGate("sample");
-  }
-
-  private static QualityGate createCustomQualityGate(String name) {
-    return qualityGateClient().create(name);
-  }
-
-  private void associateWithQualityGate(QualityGate qualityGate) {
-    wsClient.qualityGates().associateProject(new SelectWsRequest().setProjectKey("sample").setGateId(qualityGate.id()));
-  }
-
-  private static QualityGateClient qualityGateClient() {
-    return ORCHESTRATOR.getServer().adminWsClient().qualityGateClient();
-  }
-}
diff --git a/tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectSearchTest.java b/tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectSearchTest.java
deleted file mode 100644 (file)
index cd1c010..0000000
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * 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.tests.projectAdministration;
-
-import com.sonar.orchestrator.Orchestrator;
-import java.util.Date;
-import org.apache.commons.lang.time.DateUtils;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonarqube.tests.Category6Suite;
-import org.sonarqube.qa.util.Tester;
-import org.sonarqube.ws.Organizations;
-import org.sonarqube.ws.WsProjects.CreateWsResponse;
-import org.sonarqube.ws.WsProjects.SearchWsResponse;
-import org.sonarqube.ws.WsProjects.SearchWsResponse.Component;
-import org.sonarqube.ws.client.GetRequest;
-import org.sonarqube.ws.client.project.SearchWsRequest;
-
-import static java.util.Collections.singletonList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static util.ItUtils.formatDate;
-import static util.ItUtils.runProjectAnalysis;
-
-public class ProjectSearchTest {
-
-  @ClassRule
-  public static Orchestrator orchestrator = Category6Suite.ORCHESTRATOR;
-  @Rule
-  public Tester tester = new Tester(orchestrator);
-
-  @Test
-  public void search_old_projects() {
-    Organizations.Organization organization = tester.organizations().generate();
-    CreateWsResponse.Project oldProject = tester.projects().generate(organization);
-    CreateWsResponse.Project recentProject = tester.projects().generate(organization);
-    Date now = new Date();
-    Date oneYearAgo = DateUtils.addDays(now, -365);
-    Date moreThanOneYearAgo = DateUtils.addDays(now, -366);
-
-    analyzeProject(oldProject.getKey(), moreThanOneYearAgo, organization.getKey());
-    analyzeProject(recentProject.getKey(), now, organization.getKey());
-
-    SearchWsResponse result = tester.wsClient().projects().search(SearchWsRequest.builder()
-      .setOrganization(organization.getKey())
-      .setQualifiers(singletonList("TRK"))
-      .setAnalyzedBefore(formatDate(oneYearAgo)).build());
-
-    assertThat(result.getComponentsList()).extracting(Component::getKey).containsExactlyInAnyOrder(oldProject.getKey());
-  }
-
-  @Test
-  public void search_on_key_query_partial_match_case_insensitive() {
-    Organizations.Organization organization = tester.organizations().generate();
-    CreateWsResponse.Project lowerCaseProject = tester.projects().generate(organization, p -> p.setKey("project-key"));
-    CreateWsResponse.Project upperCaseProject = tester.projects().generate(organization, p -> p.setKey("PROJECT-KEY"));
-    CreateWsResponse.Project anotherProject = tester.projects().generate(organization, p -> p.setKey("another-project"));
-
-    analyzeProject(lowerCaseProject.getKey(), organization.getKey());
-    analyzeProject(upperCaseProject.getKey(), organization.getKey());
-    analyzeProject(anotherProject.getKey(), organization.getKey());
-
-    SearchWsResponse result = tester.wsClient().projects().search(SearchWsRequest.builder()
-      .setOrganization(organization.getKey())
-      .setQualifiers(singletonList("TRK"))
-      .setQuery("JeCt-K")
-      .build());
-
-    assertThat(result.getComponentsList()).extracting(Component::getKey)
-      .containsExactlyInAnyOrder(lowerCaseProject.getKey(), upperCaseProject.getKey())
-      .doesNotContain(anotherProject.getKey());
-  }
-
-  @Test
-  public void search_provisioned_projects() {
-    Organizations.Organization organization = tester.organizations().generate();
-    CreateWsResponse.Project firstProvisionedProject = tester.projects().generate(organization);
-    CreateWsResponse.Project secondProvisionedProject = tester.projects().generate(organization);
-    CreateWsResponse.Project analyzedProject = tester.projects().generate(organization);
-
-    analyzeProject(analyzedProject.getKey(), organization.getKey());
-
-    String result = tester.wsClient().wsConnector().call(new GetRequest("api/projects/provisioned")
-      .setParam("organization", organization.getKey()))
-      .failIfNotSuccessful().content();
-   SearchWsResponse searchResult = tester.wsClient().projects().search(SearchWsRequest.builder()
-     .setQualifiers(singletonList("TRK"))
-     .setOrganization(organization.getKey())
-     .setOnProvisionedOnly(true).build());
-
-    assertThat(result).contains(firstProvisionedProject.getKey(), secondProvisionedProject.getKey()).doesNotContain(analyzedProject.getKey());
-    assertThat(searchResult.getComponentsList()).extracting(Component::getKey)
-      .containsOnly(firstProvisionedProject.getKey(), secondProvisionedProject.getKey())
-      .doesNotContain(analyzedProject.getKey());
-  }
-
-  private void analyzeProject(String projectKey, Date analysisDate, String organizationKey) {
-    runProjectAnalysis(orchestrator, "shared/xoo-sample",
-      "sonar.organization", organizationKey,
-      "sonar.projectKey", projectKey,
-      "sonar.projectDate", formatDate(analysisDate),
-      "sonar.login", "admin",
-      "sonar.password", "admin");
-  }
-
-  private void analyzeProject(String projectKey, String organizationKey) {
-    analyzeProject(projectKey, new Date(), organizationKey);
-  }
-}
diff --git a/tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectVisibilityPageTest.java b/tests/src/test/java/org/sonarqube/tests/projectAdministration/ProjectVisibilityPageTest.java
deleted file mode 100644 (file)
index d5b4b12..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-/*
- * 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.tests.projectAdministration;
-
-import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.build.SonarScanner;
-import org.sonarqube.tests.Category1Suite;
-import java.sql.SQLException;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonarqube.ws.WsComponents;
-import org.sonarqube.ws.client.component.SearchProjectsRequest;
-import org.sonarqube.ws.client.permission.RemoveGroupWsRequest;
-import org.sonarqube.ws.client.project.UpdateVisibilityRequest;
-import org.sonarqube.qa.util.pageobjects.Navigation;
-import org.sonarqube.qa.util.pageobjects.ProjectsManagementPage;
-import util.user.UserRule;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static util.ItUtils.newAdminWsClient;
-import static util.ItUtils.projectDir;
-
-public class ProjectVisibilityPageTest {
-
-  @ClassRule
-  public static Orchestrator orchestrator = Category1Suite.ORCHESTRATOR;
-
-  @Rule
-  public UserRule userRule = UserRule.from(orchestrator);
-
-  private Navigation nav = Navigation.create(orchestrator);
-
-  private String adminUser;
-
-  @Before
-  public void initData() throws SQLException {
-    orchestrator.resetData();
-    adminUser = userRule.createAdminUser();
-  }
-
-  @Test
-  public void return_all_projects_even_when_no_permission() throws Exception {
-    orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-sample")).setProperties("sonar.projectKey", "sample1"));
-    orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-sample")).setProperties("sonar.projectKey", "sample2"));
-    newAdminWsClient(orchestrator).projects().updateVisibility(UpdateVisibilityRequest.builder().setProject("sample2").setVisibility("private").build());
-    // Remove 'Admin' permission for admin group on project 2 -> No one can access or admin this project, expect System Admin
-    newAdminWsClient(orchestrator).permissions().removeGroup(new RemoveGroupWsRequest().setProjectKey("sample2").setGroupName("sonar-administrators").setPermission("admin"));
-
-    nav.logIn().submitCredentials(adminUser).openProjectsManagement()
-      .shouldHaveProject("sample1")
-      .shouldHaveProject("sample2");
-  }
-
-  @Test
-  public void create_public_project() {
-    createProjectAndVerify("public");
-  }
-
-  @Test
-  public void create_private_project() {
-    createProjectAndVerify("private");
-  }
-
-  private void createProjectAndVerify(String visibility) {
-    ProjectsManagementPage page = nav.logIn().submitCredentials(adminUser, adminUser).openProjectsManagement();
-    page
-      .shouldHaveProjectsCount(0)
-      .createProject("foo", "foo", visibility)
-      .shouldHaveProjectsCount(1);
-
-    WsComponents.SearchProjectsWsResponse response = newAdminWsClient(orchestrator).components().searchProjects(
-      SearchProjectsRequest.builder().build());
-    assertThat(response.getComponentsCount()).isEqualTo(1);
-    assertThat(response.getComponents(0).getKey()).isEqualTo("foo");
-    assertThat(response.getComponents(0).getName()).isEqualTo("foo");
-    assertThat(response.getComponents(0).getVisibility()).isEqualTo(visibility);
-  }
-
-}
diff --git a/tests/src/test/java/org/sonarqube/tests/projectEvent/ProjectActivityPageTest.java b/tests/src/test/java/org/sonarqube/tests/projectEvent/ProjectActivityPageTest.java
deleted file mode 100644 (file)
index 7261e43..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-/*
- * 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.tests.projectEvent;
-
-import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.build.SonarScanner;
-import org.sonarqube.tests.Category4Suite;
-import java.util.List;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonarqube.qa.util.pageobjects.Navigation;
-import org.sonarqube.qa.util.pageobjects.ProjectActivityPage;
-import org.sonarqube.qa.util.pageobjects.ProjectAnalysisItem;
-import util.user.UserRule;
-
-import static util.ItUtils.projectDir;
-
-public class ProjectActivityPageTest {
-
-  @ClassRule
-  public static Orchestrator ORCHESTRATOR = Category4Suite.ORCHESTRATOR;
-
-  @Rule
-  public UserRule userRule = UserRule.from(ORCHESTRATOR);
-
-  private Navigation nav = Navigation.create(ORCHESTRATOR);
-
-  @Before
-  public void setUp() throws Exception {
-    ORCHESTRATOR.resetData();
-  }
-
-  @Test
-  public void should_list_snapshots() {
-    analyzeProject("shared/xoo-history-v1", "2014-10-19");
-    analyzeProject("shared/xoo-history-v2", "2014-11-13");
-
-    ProjectActivityPage page = openPage();
-    page.getAnalyses().shouldHaveSize(2);
-
-    List<ProjectAnalysisItem> analyses = page.getAnalysesAsItems();
-    analyses.get(0)
-      .shouldHaveEventWithText("1.0-SNAPSHOT")
-      .shouldNotHaveDeleteButton();
-
-    analyses.get(1)
-      .shouldHaveEventWithText("0.9-SNAPSHOT")
-      .shouldHaveDeleteButton();
-  }
-
-  @Test
-  public void add_change_delete_custom_event() {
-    analyzeProject();
-    openPage().getLastAnalysis()
-      .addCustomEvent("foo")
-      .changeFirstEvent("bar")
-      .deleteFirstEvent();
-  }
-
-  @Test
-  public void delete_analysis() {
-    analyzeProject();
-    analyzeProject();
-    ProjectActivityPage page = openPage();
-    page.getAnalyses().shouldHaveSize(2);
-    page.getFirstAnalysis().delete();
-  }
-
-  private ProjectActivityPage openPage() {
-    String userAdmin = userRule.createAdminUser();
-    nav.logIn().submitCredentials(userAdmin, userAdmin);
-    return nav.openProjectActivity("sample");
-  }
-
-  private static void analyzeProject() {
-    ORCHESTRATOR.executeBuild(SonarScanner.create(projectDir("shared/xoo-sample")));
-  }
-
-  private static void analyzeProject(String path, String date) {
-    ORCHESTRATOR.executeBuild(SonarScanner.create(projectDir(path)).setProperties("sonar.projectDate", date));
-  }
-}
diff --git a/tests/src/test/java/org/sonarqube/tests/projectSearch/LeakProjectsPageTest.java b/tests/src/test/java/org/sonarqube/tests/projectSearch/LeakProjectsPageTest.java
deleted file mode 100644 (file)
index 54ff1ce..0000000
+++ /dev/null
@@ -1,119 +0,0 @@
-/*
- * 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.tests.projectSearch;
-
-import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.build.SonarScanner;
-import org.sonarqube.tests.Category6Suite;
-import java.util.ArrayList;
-import java.util.List;
-import javax.annotation.Nullable;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonarqube.qa.util.Tester;
-import org.sonarqube.ws.Organizations.Organization;
-import org.sonarqube.qa.util.pageobjects.projects.ProjectsPage;
-
-import static com.codeborne.selenide.WebDriverRunner.url;
-import static java.util.Arrays.asList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static util.ItUtils.newProjectKey;
-import static util.ItUtils.projectDir;
-import static util.ItUtils.resetSettings;
-import static util.ItUtils.restoreProfile;
-import static util.ItUtils.setServerProperty;
-
-public class LeakProjectsPageTest {
-
-  @ClassRule
-  public static Orchestrator orchestrator = Category6Suite.ORCHESTRATOR;
-
-  @Rule
-  public Tester tester = new Tester(orchestrator);
-
-  private Organization organization;
-
-  @BeforeClass
-  public static void beforeClass() {
-    setServerProperty(orchestrator, "sonar.leak.period", "previous_version");
-  }
-
-  @AfterClass
-  public static void tearDown() {
-    resetSettings(orchestrator, null, "sonar.leak.period");
-  }
-
-  @Before
-  public void setUp() {
-    organization = tester.organizations().generate();
-    restoreProfile(orchestrator, SearchProjectsTest.class.getResource("/projectSearch/SearchProjectsTest/with-many-rules.xml"), organization.getKey());
-  }
-
-  @Test
-  public void should_display_leak_information() {
-    // This project has 0% duplication on new code
-    String projectKey2 = newProjectKey();
-    analyzeProject(projectKey2, "projectSearch/xoo-history-v1", "2016-12-31");
-    analyzeProject(projectKey2, "projectSearch/xoo-history-v2", null);
-
-    // This project has no duplication on new code
-    String projectKey1 = newProjectKey();
-    analyzeProject(projectKey1, "shared/xoo-sample", "2016-12-31");
-    analyzeProject(projectKey1, "shared/xoo-sample", null);
-
-    // Check the facets and project cards
-    ProjectsPage page = tester.openBrowser().openProjects(organization.getKey());
-    page.changePerspective("Leak");
-    assertThat(url()).endsWith("/projects?view=leak");
-    page.shouldHaveTotal(2);
-    page.getProjectByKey(projectKey2)
-      .shouldHaveMeasure("new_reliability_rating", "0A")
-      .shouldHaveMeasure("new_security_rating", "0A")
-      .shouldHaveMeasure("new_maintainability_rating", "17A")
-      .shouldHaveMeasure("new_coverage", "–")
-      .shouldHaveMeasure("new_duplicated_lines_density", "0.0%")
-      .shouldHaveMeasure("new_lines", "17");
-    page.getFacetByProperty("new_duplications")
-      .shouldHaveValue("1", "1")
-      .shouldHaveValue("2", "0")
-      .shouldHaveValue("3", "0")
-      .shouldHaveValue("4", "0")
-      .shouldHaveValue("5", "0")
-      .shouldHaveValue("6", "1");
-  }
-
-  private void analyzeProject(String projectKey, String relativePath, @Nullable String analysisDate) {
-    List<String> keyValueProperties = new ArrayList<>(asList(
-      "sonar.projectKey", projectKey,
-      "sonar.organization", organization.getKey(),
-      "sonar.profile", "with-many-rules",
-      "sonar.login", "admin", "sonar.password", "admin",
-      "sonar.scm.disabled", "false"));
-    if (analysisDate != null) {
-      keyValueProperties.add("sonar.projectDate");
-      keyValueProperties.add(analysisDate);
-    }
-    orchestrator.executeBuild(SonarScanner.create(projectDir(relativePath), keyValueProperties.toArray(new String[0])));
-  }
-}
diff --git a/tests/src/test/java/org/sonarqube/tests/projectSearch/ProjectsPageTest.java b/tests/src/test/java/org/sonarqube/tests/projectSearch/ProjectsPageTest.java
deleted file mode 100644 (file)
index 2843898..0000000
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * 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.tests.projectSearch;
-
-import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.build.SonarScanner;
-import org.junit.AfterClass;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.ClassRule;
-import org.junit.Test;
-import org.junit.rules.RuleChain;
-import org.sonarqube.qa.util.pageobjects.Navigation;
-import org.sonarqube.qa.util.pageobjects.projects.ProjectsPage;
-import org.sonarqube.tests.Category1Suite;
-import org.sonarqube.qa.util.Tester;
-import org.sonarqube.ws.WsUsers;
-import org.sonarqube.ws.client.PostRequest;
-import org.sonarqube.ws.client.WsClient;
-import org.sonarqube.ws.client.project.DeleteRequest;
-
-import static com.codeborne.selenide.Selenide.clearBrowserLocalStorage;
-import static com.codeborne.selenide.WebDriverRunner.url;
-import static org.assertj.core.api.Assertions.assertThat;
-import static util.ItUtils.projectDir;
-
-public class ProjectsPageTest {
-
-  @ClassRule
-  public static Orchestrator orchestrator = Category1Suite.ORCHESTRATOR;
-
-  private static final String PROJECT_KEY = "key-foo";
-  private static Tester tester = new Tester(orchestrator).disableOrganizations();
-
-  @ClassRule
-  public static RuleChain ruleChain = RuleChain.outerRule(orchestrator)
-    .around(tester);
-
-  @BeforeClass
-  public static void setUp() {
-    orchestrator.resetData();
-    orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-sample")).setProjectKey(PROJECT_KEY));
-    orchestrator.executeBuild(SonarScanner.create(projectDir("duplications/file-duplications")).setProjectKey("key-bar"));
-  }
-
-  @AfterClass
-  public static void tearDown() {
-    tester.wsClient().projects().delete(DeleteRequest.builder().setKey(PROJECT_KEY).build());
-    tester.wsClient().projects().delete(DeleteRequest.builder().setKey("key-bar").build());
-  }
-
-  @Before
-  public void before() {
-    clearBrowserLocalStorage();
-  }
-
-  @Test
-  public void should_display_projects() {
-    ProjectsPage page = tester.openBrowser().openProjects();
-    page.shouldHaveTotal(2);
-    page.getProjectByKey(PROJECT_KEY)
-      .shouldHaveMeasure("reliability_rating", "A")
-      .shouldHaveMeasure("security_rating", "A")
-      .shouldHaveMeasure("sqale_rating", "A")
-      .shouldHaveMeasure("duplicated_lines_density", "0.0%")
-      .shouldHaveMeasure("ncloc", "13")
-      .shouldHaveMeasure("ncloc", "Xoo");
-  }
-
-  @Test
-  public void should_display_facets() {
-    ProjectsPage page = tester.openBrowser().openProjects();
-    page.getFacetByProperty("duplications")
-      .shouldHaveValue("1", "1")
-      .shouldHaveValue("2", "1")
-      .shouldHaveValue("3", "1")
-      .shouldHaveValue("4", "1")
-      .shouldHaveValue("5", "1")
-      .shouldHaveValue("6", "0");
-  }
-
-  @Test
-  public void should_filter_using_facet() {
-    ProjectsPage page = tester.openBrowser().openProjects();
-    page.shouldHaveTotal(2);
-    page.getFacetByProperty("duplications").selectValue("3");
-    page.shouldHaveTotal(1);
-  }
-
-  @Test
-  public void should_open_default_page() {
-    // default page can be "All Projects" or "Favorite Projects" depending on your last choice
-    Navigation nav = tester.openBrowser();
-    ProjectsPage page = nav.openProjects();
-
-    // all projects for anonymous user with default sorting to analysis date
-    page.shouldHaveTotal(2).shouldDisplayAllProjectsWidthSort("-analysis_date");
-
-    // all projects by default for logged in user
-    WsUsers.CreateWsResponse.User administrator = tester.users().generateAdministrator();
-    page = nav.logIn().submitCredentials(administrator.getLogin()).openProjects();
-    page.shouldHaveTotal(2).shouldDisplayAllProjects();
-
-    // favorite one project
-    WsClient administratorWsClient = tester.as(administrator.getLogin()).wsClient();
-    administratorWsClient.favorites().add(PROJECT_KEY);
-    page = nav.openProjects();
-    page.shouldHaveTotal(1).shouldDisplayFavoriteProjects();
-
-    // un-favorite this project
-    administratorWsClient.favorites().remove(PROJECT_KEY);
-    page = nav.openProjects();
-    page.shouldHaveTotal(2).shouldDisplayAllProjects();
-
-    // select favorite
-    page.selectFavoriteProjects();
-    page = nav.openProjects();
-    page.shouldHaveTotal(0).shouldDisplayFavoriteProjects();
-
-    // select all
-    page.selectAllProjects();
-    page = nav.openProjects();
-    page.shouldHaveTotal(2).shouldDisplayAllProjects();
-  }
-
-  @Test
-  public void should_add_language_to_facet() {
-    ProjectsPage page = tester.openBrowser().openProjects();
-    page.getFacetByProperty("languages")
-      .selectOptionItem("xoo2")
-      .shouldHaveValue("xoo2", "0");
-  }
-
-  @Test
-  public void should_add_tag_to_facet() {
-    // Add some tags to this project
-    tester.wsClient().wsConnector().call(
-      new PostRequest("api/project_tags/set")
-        .setParam("project", PROJECT_KEY)
-        .setParam("tags", "aa,bb,cc,dd,ee,ff,gg,hh,ii,jj,zz"));
-
-    ProjectsPage page = tester.openBrowser().openProjects();
-    page.getFacetByProperty("tags")
-      .shouldHaveValue("aa", "1")
-      .shouldHaveValue("ii", "1")
-      .selectOptionItem("zz")
-      .shouldHaveValue("zz", "1");
-  }
-
-  @Test
-  public void should_switch_between_perspectives() {
-    WsUsers.CreateWsResponse.User administrator = tester.users().generateAdministrator();
-    ProjectsPage page = tester.openBrowser()
-      .logIn().submitCredentials(administrator.getLogin())
-      .openProjects();
-    page.changePerspective("Risk");
-    assertThat(url()).endsWith("/projects?view=visualizations&visualization=risk");
-    page.changePerspective("Leak");
-    assertThat(url()).endsWith("/projects?view=leak");
-  }
-
-  @Test
-  public void should_sort_by_facet() {
-    ProjectsPage page = tester.openBrowser().openProjects();
-    page.sortProjects("Duplications");
-    page.getProjectByIdx(0).shouldHaveMeasure("duplicated_lines_density", "63.7%");
-    page.invertSorting();
-    page.getProjectByIdx(0).shouldHaveMeasure("duplicated_lines_density", "0.0%");
-  }
-
-  @Test
-  public void should_search_for_project() {
-    ProjectsPage page = tester.openBrowser().openProjects();
-    page.searchProject("s").shouldHaveTotal(2);
-    page.searchProject("sam").shouldHaveTotal(1);
-  }
-
-  @Test
-  public void should_search_for_project_and_keep_other_filters() {
-    ProjectsPage page = tester.openBrowser().openProjects();
-    page.shouldHaveTotal(2);
-    page.getFacetByProperty("duplications").selectValue("3");
-    page.shouldHaveTotal(1);
-    page.searchProject("sample").shouldHaveTotal(0);
-  }
-
-  @Test
-  public void should_open_permalink() {
-    String user = tester.users().generate().getLogin();
-    Navigation nav = tester.openBrowser().logIn().submitCredentials(user);
-
-    // make a search, so its parameters saved to local storage
-    nav.openProjects().changePerspective("Leak");
-
-    // change a page
-    nav.openHome();
-
-    // open a permalink to a particular visualization, it must be kept
-    nav.openProjectsWithQuery("view=visualizations&visualization=coverage");
-    assertThat(url()).contains("view=visualizations&visualization=coverage");
-  }
-}
diff --git a/tests/src/test/java/org/sonarqube/tests/projectSearch/SearchProjectsTest.java b/tests/src/test/java/org/sonarqube/tests/projectSearch/SearchProjectsTest.java
deleted file mode 100644 (file)
index 2e4e6f1..0000000
+++ /dev/null
@@ -1,306 +0,0 @@
-/*
- * 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.tests.projectSearch;
-
-import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.build.SonarScanner;
-import org.sonarqube.tests.Category6Suite;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import org.assertj.core.groups.Tuple;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonarqube.qa.util.Tester;
-import org.sonarqube.ws.Common;
-import org.sonarqube.ws.Organizations.Organization;
-import org.sonarqube.ws.WsComponents.Component;
-import org.sonarqube.ws.WsComponents.SearchProjectsWsResponse;
-import org.sonarqube.ws.client.component.SearchProjectsRequest;
-import org.sonarqube.ws.client.project.CreateRequest;
-
-import static java.util.Arrays.asList;
-import static java.util.Collections.singletonList;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.groups.Tuple.tuple;
-import static util.ItUtils.concat;
-import static util.ItUtils.newProjectKey;
-import static util.ItUtils.projectDir;
-import static util.ItUtils.restoreProfile;
-import static util.ItUtils.sanitizeTimezones;
-import static util.ItUtils.setServerProperty;
-
-/**
- * Tests WS api/components/search_projects
- */
-public class SearchProjectsTest {
-
-  @ClassRule
-  public static Orchestrator orchestrator = Category6Suite.ORCHESTRATOR;
-
-  @Rule
-  public Tester tester = new Tester(orchestrator);
-
-  private Organization organization;
-
-  @Before
-  public void setUp() {
-    organization = tester.organizations().generate();
-    restoreProfile(orchestrator, SearchProjectsTest.class.getResource("/projectSearch/SearchProjectsTest/with-many-rules.xml"), organization.getKey());
-  }
-
-  @Test
-  public void filter_projects_by_measure_values() throws Exception {
-    String projectKey = newProjectKey();
-    analyzeProject(projectKey, "shared/xoo-sample");
-
-    verifyFilterMatches(projectKey, "ncloc > 1");
-    verifyFilterMatches(projectKey, "ncloc > 1 and duplicated_lines_density <= 100");
-    verifyFilterDoesNotMatch("ncloc <= 1");
-  }
-
-  @Test
-  public void find_projects_with_no_data() throws Exception {
-    String projectKey = newProjectKey();
-    analyzeProject(projectKey, "shared/xoo-sample");
-
-    verifyFilterMatches(projectKey, "coverage = NO_DATA");
-    verifyFilterDoesNotMatch("ncloc = NO_DATA");
-  }
-
-  @Test
-  public void provisioned_projects_should_be_included_to_results() throws Exception {
-    String projectKey = newProjectKey();
-    tester.wsClient().projects().create(CreateRequest.builder().setKey(projectKey).setName(projectKey).setOrganization(organization.getKey()).build());
-
-    SearchProjectsWsResponse response = searchProjects(SearchProjectsRequest.builder().setOrganization(organization.getKey()).build());
-
-    assertThat(response.getComponentsList()).extracting(Component::getKey).containsOnly(projectKey);
-  }
-
-  @Test
-  public void return_leak_period_date() throws Exception {
-    setServerProperty(orchestrator, "sonar.leak.period", "previous_version");
-    // This project has a leak period
-    String projectKey1 = newProjectKey();
-    analyzeProject(projectKey1, "shared/xoo-sample", "sonar.projectDate", "2016-12-31");
-    analyzeProject(projectKey1, "shared/xoo-sample");
-    // This project has only one analysis, so no leak period
-    String projectKey2 = newProjectKey();
-    analyzeProject(projectKey2, "shared/xoo-sample");
-    // This project is provisioned, so has no leak period
-    String projectKey3 = newProjectKey();
-    tester.wsClient().projects().create(CreateRequest.builder().setKey(projectKey3).setName(projectKey3).setOrganization(organization.getKey()).build());
-
-    SearchProjectsWsResponse response = searchProjects(
-      SearchProjectsRequest.builder().setAdditionalFields(singletonList("leakPeriodDate")).setOrganization(organization.getKey()).build());
-
-    assertThat(response.getComponentsList()).extracting(Component::getKey, Component::hasLeakPeriodDate)
-      .containsOnly(
-        tuple(projectKey1, true),
-        tuple(projectKey2, false),
-        tuple(projectKey3, false));
-    Component project1 = response.getComponentsList().stream().filter(component -> component.getKey().equals(projectKey1)).findFirst()
-      .orElseThrow(() -> new IllegalStateException("Project1 is not found"));
-    assertThat(sanitizeTimezones(project1.getLeakPeriodDate())).isEqualTo("2016-12-31T00:00:00+0000");
-  }
-
-  @Test
-  public void filter_by_text_query() throws IOException {
-    analyzeProject("project1", "shared/xoo-sample", "sonar.projectName", "apachee");
-    analyzeProject("project2", "shared/xoo-sample", "sonar.projectName", "Apache");
-    analyzeProject("project3", "shared/xoo-multi-modules-sample", "sonar.projectName", "Apache Foundation");
-    analyzeProject("project4", "shared/xoo-multi-modules-sample", "sonar.projectName", "Windows");
-
-    // Search only by text query
-    assertThat(searchProjects("query = \"apache\"").getComponentsList()).extracting(Component::getKey).containsExactly("project2", "project3", "project1");
-    assertThat(searchProjects("query = \"pAch\"").getComponentsList()).extracting(Component::getKey).containsExactly("project2", "project3", "project1");
-    assertThat(searchProjects("query = \"hee\"").getComponentsList()).extracting(Component::getKey).containsExactly("project1");
-    assertThat(searchProjects("query = \"project1\"").getComponentsList()).extracting(Component::getKey).containsExactly("project1");
-    assertThat(searchProjects("query = \"unknown\"").getComponentsList()).isEmpty();
-
-    // Search by metric criteria and text query
-    assertThat(searchProjects(SearchProjectsRequest.builder().setFilter("query = \"pAch\" AND ncloc > 50").build()).getComponentsList())
-      .extracting(Component::getKey).containsExactly("project3");
-    assertThat(searchProjects(SearchProjectsRequest.builder().setFilter("query = \"nd\" AND ncloc > 50").build()).getComponentsList())
-      .extracting(Component::getKey).containsExactly("project3", "project4");
-    assertThat(searchProjects(SearchProjectsRequest.builder().setFilter("query = \"unknown\" AND ncloc > 50").build()).getComponentsList()).isEmpty();
-    ;
-
-    // Check facets
-    assertThat(searchProjects(SearchProjectsRequest.builder().setFilter("query = \"apache\"").setFacets(singletonList("ncloc")).build()).getFacets().getFacets(0).getValuesList())
-      .extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
-      .containsOnly(tuple("*-1000.0", 3L), tuple("1000.0-10000.0", 0L), tuple("10000.0-100000.0", 0L), tuple("100000.0-500000.0", 0L), tuple("500000.0-*", 0L));
-    assertThat(searchProjects(SearchProjectsRequest.builder().setFilter("query = \"unknown\"").setFacets(singletonList("ncloc")).build()).getFacets().getFacets(0)
-      .getValuesList()).extracting(Common.FacetValue::getVal, Common.FacetValue::getCount)
-        .containsOnly(tuple("*-1000.0", 0L), tuple("1000.0-10000.0", 0L), tuple("10000.0-100000.0", 0L), tuple("100000.0-500000.0", 0L), tuple("500000.0-*", 0L));
-  }
-
-  @Test
-  public void should_return_facets() throws Exception {
-    analyzeProject(newProjectKey(), "shared/xoo-sample");
-    analyzeProject(newProjectKey(), "shared/xoo-multi-modules-sample");
-
-    SearchProjectsWsResponse response = searchProjects(SearchProjectsRequest.builder().setOrganization(organization.getKey()).setFacets(asList(
-      "alert_status",
-      "coverage",
-      "duplicated_lines_density",
-      "languages",
-      "ncloc",
-      "reliability_rating",
-      "security_rating",
-      "sqale_rating",
-      "tags")).build());
-
-    checkFacet(response, "alert_status",
-      tuple("OK", 2L),
-      tuple("WARN", 0L),
-      tuple("ERROR", 0L));
-    checkFacet(response, "coverage",
-      tuple("NO_DATA", 2L),
-      tuple("*-30.0", 0L),
-      tuple("30.0-50.0", 0L),
-      tuple("50.0-70.0", 0L),
-      tuple("70.0-80.0", 0L),
-      tuple("80.0-*", 0L));
-    checkFacet(response, "duplicated_lines_density",
-      tuple("NO_DATA", 0L),
-      tuple("*-3.0", 2L),
-      tuple("3.0-5.0", 0L),
-      tuple("5.0-10.0", 0L),
-      tuple("10.0-20.0", 0L),
-      tuple("20.0-*", 0L));
-    checkFacet(response, "languages",
-      tuple("xoo", 2L));
-    checkFacet(response, "ncloc",
-      tuple("*-1000.0", 2L),
-      tuple("1000.0-10000.0", 0L),
-      tuple("10000.0-100000.0", 0L),
-      tuple("100000.0-500000.0", 0L),
-      tuple("500000.0-*", 0L));
-    checkFacet(response, "reliability_rating",
-      tuple("1", 2L),
-      tuple("2", 0L),
-      tuple("3", 0L),
-      tuple("4", 0L),
-      tuple("5", 0L));
-    checkFacet(response, "security_rating",
-      tuple("1", 2L),
-      tuple("2", 0L),
-      tuple("3", 0L),
-      tuple("4", 0L),
-      tuple("5", 0L));
-    checkFacet(response, "sqale_rating",
-      tuple("1", 0L),
-      tuple("2", 0L),
-      tuple("3", 0L),
-      tuple("4", 2L),
-      tuple("5", 0L));
-    checkFacet(response, "tags");
-  }
-
-  @Test
-  public void should_return_facets_on_leak() throws Exception {
-    setServerProperty(orchestrator, "sonar.leak.period", "previous_version");
-    // This project has no duplication on new code
-    String projectKey1 = newProjectKey();
-    analyzeProject(projectKey1, "shared/xoo-sample", "sonar.projectDate", "2016-12-31");
-    analyzeProject(projectKey1, "shared/xoo-sample");
-    // This project has 0% duplication on new code
-    String projectKey2 = newProjectKey();
-    analyzeProject(projectKey2, "projectSearch/xoo-history-v1", "sonar.projectDate", "2016-12-31");
-    analyzeProject(projectKey2, "projectSearch/xoo-history-v2");
-
-    SearchProjectsWsResponse response = searchProjects(SearchProjectsRequest.builder().setOrganization(organization.getKey()).setFacets(asList(
-      "new_reliability_rating", "new_security_rating", "new_maintainability_rating", "new_coverage", "new_duplicated_lines_density", "new_lines")).build());
-
-    checkFacet(response, "new_reliability_rating",
-      tuple("1", 2L),
-      tuple("2", 0L),
-      tuple("3", 0L),
-      tuple("4", 0L),
-      tuple("5", 0L));
-    checkFacet(response, "new_security_rating",
-      tuple("1", 2L),
-      tuple("2", 0L),
-      tuple("3", 0L),
-      tuple("4", 0L),
-      tuple("5", 0L));
-    checkFacet(response, "new_maintainability_rating",
-      tuple("1", 2L),
-      tuple("2", 0L),
-      tuple("3", 0L),
-      tuple("4", 0L),
-      tuple("5", 0L));
-    checkFacet(response, "new_coverage",
-      tuple("NO_DATA", 2L),
-      tuple("*-30.0", 0L),
-      tuple("30.0-50.0", 0L),
-      tuple("50.0-70.0", 0L),
-      tuple("70.0-80.0", 0L),
-      tuple("80.0-*", 0L));
-    checkFacet(response, "new_duplicated_lines_density",
-      tuple("NO_DATA", 1L),
-      tuple("*-3.0", 1L),
-      tuple("3.0-5.0", 0L),
-      tuple("5.0-10.0", 0L),
-      tuple("10.0-20.0", 0L),
-      tuple("20.0-*", 0L));
-    checkFacet(response, "new_lines",
-      tuple("*-1000.0", 1L),
-      tuple("1000.0-10000.0", 0L),
-      tuple("10000.0-100000.0", 0L),
-      tuple("100000.0-500000.0", 0L),
-      tuple("500000.0-*", 0L));
-  }
-
-  private void checkFacet(SearchProjectsWsResponse response, String facetKey, Tuple... values) {
-    Common.Facet facet = response.getFacets().getFacetsList().stream().filter(f -> f.getProperty().equals(facetKey)).findAny().get();
-    assertThat(facet.getValuesList()).extracting(Common.FacetValue::getVal, Common.FacetValue::getCount).containsExactlyInAnyOrder(values);
-  }
-
-  private void analyzeProject(String projectKey, String relativePath, String... properties) {
-    List<String> keyValueProperties = new ArrayList<>(asList(
-      "sonar.projectKey", projectKey,
-      "sonar.organization", organization.getKey(),
-      "sonar.profile", "with-many-rules",
-      "sonar.login", "admin", "sonar.password", "admin",
-      "sonar.scm.disabled", "false"));
-    orchestrator.executeBuild(SonarScanner.create(projectDir(relativePath), concat(keyValueProperties.toArray(new String[0]), properties)));
-  }
-
-  private SearchProjectsWsResponse searchProjects(String filter) throws IOException {
-    return searchProjects(SearchProjectsRequest.builder().setOrganization(organization.getKey()).setFilter(filter).build());
-  }
-
-  private SearchProjectsWsResponse searchProjects(SearchProjectsRequest request) throws IOException {
-    return tester.wsClient().components().searchProjects(request);
-  }
-
-  private void verifyFilterMatches(String projectKey, String filter) throws IOException {
-    assertThat(searchProjects(filter).getComponentsList()).extracting(Component::getKey).containsOnly(projectKey);
-  }
-
-  private void verifyFilterDoesNotMatch(String filter) throws IOException {
-    assertThat(searchProjects(filter).getComponentsCount()).isZero();
-  }
-}
diff --git a/tests/src/test/java/org/sonarqube/tests/qualityGate/ProjectQualityGatePageTest.java b/tests/src/test/java/org/sonarqube/tests/qualityGate/ProjectQualityGatePageTest.java
new file mode 100644 (file)
index 0000000..d47377f
--- /dev/null
@@ -0,0 +1,162 @@
+/*
+ * 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.tests.qualityGate;
+
+import com.codeborne.selenide.Condition;
+import com.codeborne.selenide.Selenide;
+import com.codeborne.selenide.SelenideElement;
+import com.sonar.orchestrator.Orchestrator;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Ignore;
+import org.junit.Rule;
+import org.junit.Test;
+import org.openqa.selenium.Keys;
+import org.sonar.wsclient.qualitygate.QualityGate;
+import org.sonar.wsclient.qualitygate.QualityGateClient;
+import org.sonarqube.qa.util.Tester;
+import org.sonarqube.qa.util.pageobjects.Navigation;
+import org.sonarqube.qa.util.pageobjects.ProjectQualityGatePage;
+import org.sonarqube.ws.client.PostRequest;
+import org.sonarqube.ws.client.qualitygate.SelectWsRequest;
+
+public class ProjectQualityGatePageTest {
+
+  @ClassRule
+  public static Orchestrator orchestrator = QualityGateSuite.ORCHESTRATOR;
+
+  @Rule
+  public Tester tester = new Tester(orchestrator)
+    // all the tests of QualityGateSuite must disable organizations
+    .disableOrganizations();
+
+  @Before
+  public void setUp() {
+    tester.wsClient().wsConnector().call(new PostRequest("api/projects/create")
+      .setParam("name", "Sample")
+      .setParam("key", "sample"));
+    defaultGate = qualityGateClient().list().defaultGate();
+  }
+
+  private QualityGate defaultGate;
+
+  @After
+  public void tearDown() {
+    if (defaultGate != null) {
+      qualityGateClient().setDefault(defaultGate.id());
+    }
+  }
+
+  @Test
+  public void should_display_default() {
+    QualityGate customQualityGate = createCustomQualityGate("should_display_default");
+    qualityGateClient().setDefault(customQualityGate.id());
+
+    try {
+      ProjectQualityGatePage page = openPage();
+      SelenideElement selectedQualityGate = page.getSelectedQualityGate();
+      selectedQualityGate.should(Condition.text("Default"));
+      selectedQualityGate.should(Condition.text(customQualityGate.name()));
+    } finally {
+      qualityGateClient().unsetDefault();
+      qualityGateClient().destroy(customQualityGate.id());
+    }
+  }
+
+  @Test
+  public void should_display_custom() {
+    QualityGate customQualityGate = createCustomQualityGate("should_display_custom");
+    associateWithQualityGate(customQualityGate);
+
+    ProjectQualityGatePage page = openPage();
+    SelenideElement selectedQualityGate = page.getSelectedQualityGate();
+    selectedQualityGate.shouldNot(Condition.text("Default"));
+    selectedQualityGate.should(Condition.text(customQualityGate.name()));
+  }
+
+  @Test
+  public void should_display_none() {
+    qualityGateClient().unsetDefault();
+
+    ProjectQualityGatePage page = openPage();
+    page.assertNotSelected();
+  }
+
+  @Test
+  public void should_set_custom() {
+    QualityGate customQualityGate = createCustomQualityGate("should_set_custom");
+
+    ProjectQualityGatePage page = openPage();
+    page.setQualityGate(customQualityGate.name());
+
+    SelenideElement selectedQualityGate = page.getSelectedQualityGate();
+    selectedQualityGate.should(Condition.text(customQualityGate.name()));
+  }
+
+  @Test
+  public void should_set_default() {
+    QualityGate customQualityGate = createCustomQualityGate("should_set_default");
+    qualityGateClient().setDefault(customQualityGate.id());
+
+    try {
+      ProjectQualityGatePage page = openPage();
+      page.setQualityGate(customQualityGate.name());
+
+      SelenideElement selectedQualityGate = page.getSelectedQualityGate();
+      selectedQualityGate.should(Condition.text("Default"));
+      selectedQualityGate.should(Condition.text(customQualityGate.name()));
+    } finally {
+      qualityGateClient().unsetDefault();
+      qualityGateClient().destroy(customQualityGate.id());
+    }
+  }
+
+  @Test
+  @Ignore
+  public void should_set_none() {
+    qualityGateClient().unsetDefault();
+    QualityGate customQualityGate = createCustomQualityGate("should_set_none");
+    associateWithQualityGate(customQualityGate);
+
+    ProjectQualityGatePage page = openPage();
+    Selenide.$(".Select-input input").sendKeys(Keys.UP, Keys.UP, Keys.UP, Keys.ENTER);
+
+    page.assertNotSelected();
+  }
+
+  private ProjectQualityGatePage openPage() {
+    tester.wsClient().users().skipOnboardingTutorial();
+    Navigation navigation = tester.openBrowser().logIn().submitCredentials("admin");
+    return navigation.openProjectQualityGate("sample");
+  }
+
+  private QualityGate createCustomQualityGate(String name) {
+    return qualityGateClient().create(name);
+  }
+
+  private void associateWithQualityGate(QualityGate qualityGate) {
+    tester.wsClient().qualityGates().associateProject(new SelectWsRequest().setProjectKey("sample").setGateId(qualityGate.id()));
+  }
+
+  private QualityGateClient qualityGateClient() {
+    return orchestrator.getServer().adminWsClient().qualityGateClient();
+  }
+}
index ce4632b235a6cb139931e9e410c9b611e9573c8e..616f9bf35ac794b5d9c785c9fc637596c47b997b 100644 (file)
@@ -29,6 +29,7 @@ import static util.ItUtils.xooPlugin;
 
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
+  ProjectQualityGatePageTest.class,
   QualityGateTest.class,
   QualityGateUiTest.class,
   QualityGateNotificationTest.class,
diff --git a/tests/src/test/java/org/sonarqube/tests/source/ProjectCodeTest.java b/tests/src/test/java/org/sonarqube/tests/source/ProjectCodeTest.java
deleted file mode 100644 (file)
index 974ea41..0000000
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.tests.source;
-
-import com.sonar.orchestrator.Orchestrator;
-import com.sonar.orchestrator.build.SonarScanner;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
-import org.sonarqube.qa.util.Tester;
-import org.sonarqube.ws.WsProjects.CreateWsResponse.Project;
-
-import static util.ItUtils.projectDir;
-
-public class ProjectCodeTest {
-
-  @ClassRule
-  public static Orchestrator orchestrator = SourceSuite.ORCHESTRATOR;
-
-  @Rule
-  public Tester tester = new Tester(orchestrator);
-
-  @Test
-  public void browse() {
-    Project project = tester.projects().generate(null);
-    executeAnalysis(project);
-
-    tester.openBrowser().openCode(project.getKey())
-      .shouldHaveComponent("src/main/xoo/sample")
-      .openFirstComponent()
-      .shouldHaveComponent("Sample.xoo")
-      .openFirstComponent()
-      .shouldHaveCode("public class Sample")
-      .shouldHaveBreadcrumbs(project.getName(), "src/main/xoo/sample", "Sample.xoo");
-  }
-
-  @Test
-  public void search() {
-    Project project = tester.projects().generate(null);
-    executeAnalysis(project);
-
-    tester.openBrowser().openCode(project.getKey())
-      .shouldHaveComponent(project.getName())
-      .search("xoo")
-      .shouldSearchResult("Sample.xoo");
-  }
-
-  @Test
-  public void permalink() {
-    Project project = tester.projects().generate(null);
-    executeAnalysis(project);
-
-    tester.openBrowser().openCode(project.getKey(), project.getKey() + "%3Asrc%2Fmain%2Fxoo%2Fsample%2FSample.xoo")
-      .shouldHaveCode("public class Sample")
-      .shouldHaveBreadcrumbs(project.getName(), "src/main/xoo/sample", "Sample.xoo");
-  }
-
-  @Test
-  public void expand_root_dir() {
-    Project project = tester.projects().generate(null);
-    executeAnalysis(project, "shared/xoo-sample-with-root-dir");
-
-    tester.openBrowser().openCode(project.getKey())
-      .shouldHaveComponent("Hello.xoo")
-      .shouldHaveComponent("src/main/xoo/sample");
-  }
-
-  private void executeAnalysis(Project project, String path) {
-    orchestrator.executeBuild(
-      SonarScanner.create(projectDir(path))
-        .setProjectKey(project.getKey())
-        .setProjectName(project.getName()));
-  }
-
-  private void executeAnalysis(Project project) {
-    executeAnalysis(project, "shared/xoo-sample");
-  }
-}
index 90fb2a729882a2c773141a94e5ca773c438f450a..a46823621c3e22fc0ff665d22872374780582f64 100644 (file)
@@ -29,7 +29,6 @@ import static util.ItUtils.xooPlugin;
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
   EncodingTest.class,
-  ProjectCodeTest.class,
   ScmTest.class,
   SourceViewerTest.class
 })
index 050960d843fc44281a0177bb5ab8865456050cf0..85e25b9e29c107df165d136795de705f60b6199a 100644 (file)
@@ -21,19 +21,18 @@ package org.sonarqube.tests.ui;
 
 import com.sonar.orchestrator.Orchestrator;
 import com.sonar.orchestrator.build.SonarScanner;
-import org.sonarqube.tests.Category4Suite;
 import java.util.Map;
-import org.junit.After;
-import org.junit.Before;
 import org.junit.ClassRule;
+import org.junit.Rule;
 import org.junit.Test;
+import org.sonarqube.qa.util.Tester;
+import org.sonarqube.qa.util.pageobjects.Navigation;
+import org.sonarqube.tests.Category4Suite;
 import org.sonarqube.ws.client.GetRequest;
 import org.sonarqube.ws.client.WsResponse;
-import org.sonarqube.qa.util.pageobjects.Navigation;
 import util.ItUtils;
 
 import static com.codeborne.selenide.Condition.exist;
-import static com.codeborne.selenide.Condition.hasText;
 import static com.codeborne.selenide.Condition.text;
 import static com.codeborne.selenide.Condition.visible;
 import static com.codeborne.selenide.Selenide.$;
@@ -41,60 +40,56 @@ import static com.codeborne.selenide.Selenide.$$;
 import static com.codeborne.selenide.WebDriverRunner.url;
 import static org.assertj.core.api.Assertions.assertThat;
 import static util.ItUtils.projectDir;
-import static util.ItUtils.resetSettings;
-import static util.ItUtils.setServerProperty;
 
 public class UiTest {
 
   @ClassRule
-  public static final Orchestrator ORCHESTRATOR = Category4Suite.ORCHESTRATOR;
-
-  private Navigation nav = Navigation.create(ORCHESTRATOR);
+  public static final Orchestrator orchestrator = Category4Suite.ORCHESTRATOR;
 
-  @Before
-  @After
-  public void resetData() throws Exception {
-    resetSettings(ORCHESTRATOR, null, "sonar.forceAuthentication");
-  }
+  @Rule
+  public Tester tester = new Tester(orchestrator).disableOrganizations();
 
   @Test
   public void footer_contains_information() {
-    nav.getFooter()
-      .should(hasText("Documentation"))
-      .should(hasText("SonarSource SA"));
+    tester.openBrowser().getFooter()
+      .should(text("Documentation"))
+      .should(text("SonarSource SA"));
   }
 
   @Test
   public void footer_contains_version() {
-    WsResponse status = ItUtils.newAdminWsClient(ORCHESTRATOR).wsConnector().call(new GetRequest("api/navigation/global"));
+    WsResponse status = tester.wsClient().wsConnector().call(new GetRequest("api/navigation/global"));
     Map<String, Object> statusMap = ItUtils.jsonToMap(status.content());
 
-    nav.getFooter().should(hasText((String) statusMap.get("version")));
+    tester.openBrowser().getFooter()
+      .should(text((String) statusMap.get("version")));
   }
 
   @Test
   public void footer_doesnt_contains_version_on_login_page() {
-    WsResponse status = ItUtils.newAdminWsClient(ORCHESTRATOR).wsConnector().call(new GetRequest("api/navigation/global"));
+    WsResponse status = tester.wsClient().wsConnector().call(new GetRequest("api/navigation/global"));
     Map<String, Object> statusMap = ItUtils.jsonToMap(status.content());
 
-    nav.openLogin();
-    nav.getFooter().shouldNot(hasText((String) statusMap.get("version")));
+    Navigation navigation = tester.openBrowser();
+    navigation.openLogin();
+    navigation.getFooter().shouldNot(text((String) statusMap.get("version")));
   }
 
   @Test
   public void footer_doesnt_contains_about_when_not_logged_in() {
-    setServerProperty(ORCHESTRATOR, "sonar.forceAuthentication", "true");
-    nav.openLogin();
-    nav.getFooter()
-      .shouldNot(hasText("About"))
-      .shouldNot(hasText("Web API"));
+    tester.settings().setGlobalSettings("sonar.forceAuthentication", "true");
+    Navigation navigation = tester.openBrowser();
+    navigation.openLogin();
+    navigation.getFooter()
+      .shouldNot(text("About"))
+      .shouldNot(text("Web API"));
   }
 
   @Test
   public void many_page_transitions() {
-    analyzeSampleProject();
+    analyzeXooSample();
 
-    nav.open("/about");
+    tester.openBrowser().open("/about");
 
     // on about page
     $(".about-page-projects-link")
@@ -145,13 +140,13 @@ public class UiTest {
   public void markdown_help() {
     String tags[] = {"strong", "a", "ul", "ol", "h1", "code", "pre", "blockquote"};
 
-    nav.open("/markdown/help");
+    tester.openBrowser().open("/markdown/help");
     for (String tag : tags) {
       $(tag).shouldBe(visible);
     }
   }
 
-  private static void analyzeSampleProject() {
-    ORCHESTRATOR.executeBuild(SonarScanner.create(projectDir("shared/xoo-sample")));
+  private static void analyzeXooSample() {
+    orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-sample")));
   }
 }