From: Stas Vilchik Date: Thu, 1 Sep 2016 15:30:34 +0000 (+0200) Subject: SONAR-5856 Rewrite Settings page (#1163) X-Git-Tag: 6.1-RC1~243 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=d2da7f30d512284c943b82abc135483b59b85536;p=sonarqube.git SONAR-5856 Rewrite Settings page (#1163) --- diff --git a/it/it-tests/src/test/java/it/Category1Suite.java b/it/it-tests/src/test/java/it/Category1Suite.java index 18f2c01a352..8f1a4cbe3a0 100644 --- a/it/it-tests/src/test/java/it/Category1Suite.java +++ b/it/it-tests/src/test/java/it/Category1Suite.java @@ -49,7 +49,6 @@ import it.qualityGate.QualityGateTest; import it.qualityGate.QualityGateUiTest; import it.settings.PropertySetsTest; import it.settings.SettingsTest; -import it.settings.SubCategoriesTest; import it.sourceCode.EncodingTest; import it.sourceCode.HighlightingTest; import it.sourceCode.ProjectCodeTest; @@ -71,7 +70,6 @@ import static util.ItUtils.xooPlugin; BackgroundTasksTest.class, // settings PropertySetsTest.class, - SubCategoriesTest.class, SettingsTest.class, // i18n I18nTest.class, diff --git a/it/it-tests/src/test/java/it/componentDashboard/DashboardTest.java b/it/it-tests/src/test/java/it/componentDashboard/DashboardTest.java index 4ef6c3000e9..2f903b05fb6 100644 --- a/it/it-tests/src/test/java/it/componentDashboard/DashboardTest.java +++ b/it/it-tests/src/test/java/it/componentDashboard/DashboardTest.java @@ -70,9 +70,6 @@ public class DashboardTest { // SONAR-2073 "/componentDashboard/DashboardTest/global_dashboard/filter-widget-anonymous.html", - // SONAR-3460 - "/componentDashboard/DashboardTest/global_dashboard/global-admin-dashboards.html", - // SONAR-3461 "/componentDashboard/DashboardTest/global_dashboard/default-dashboards.html", diff --git a/it/it-tests/src/test/java/it/measureHistory/DifferentialPeriodsTest.java b/it/it-tests/src/test/java/it/measureHistory/DifferentialPeriodsTest.java index 0cd55995418..00a02366502 100644 --- a/it/it-tests/src/test/java/it/measureHistory/DifferentialPeriodsTest.java +++ b/it/it-tests/src/test/java/it/measureHistory/DifferentialPeriodsTest.java @@ -21,7 +21,6 @@ package it.measureHistory; import com.sonar.orchestrator.Orchestrator; import com.sonar.orchestrator.locator.FileLocation; -import com.sonar.orchestrator.selenium.Selenese; import it.Category1Suite; import java.util.Date; import java.util.List; @@ -32,8 +31,8 @@ import org.junit.Test; import org.sonar.wsclient.services.Measure; import org.sonar.wsclient.services.Resource; import org.sonar.wsclient.services.ResourceQuery; +import pageobjects.Navigation; import util.ItUtils; -import util.selenium.SeleneseTest; import static org.apache.commons.lang.time.DateUtils.addDays; import static org.assertj.core.api.Assertions.assertThat; @@ -133,9 +132,8 @@ public class DifferentialPeriodsTest { assertThat(measures.get(0).getVariation1()).isEqualTo(17); // Check on ui that it's possible to define leak period on project - new SeleneseTest(Selenese.builder().setHtmlTestsInClasspath("define-leak-period-on-project", - "/measureHistory/DifferentialPeriodsTest/define-leak-period-on-project.html" - ).build()).runOn(orchestrator); + Navigation.get(orchestrator).openHomepage().logIn().asAdmin().openSettings("sample") + .assertSettingDisplayed("sonar.timemachine.period1"); } /** diff --git a/it/it-tests/src/test/java/it/projectAdministration/ProjectAdministrationTest.java b/it/it-tests/src/test/java/it/projectAdministration/ProjectAdministrationTest.java index 50a3f12b3b4..5026725cbc8 100644 --- a/it/it-tests/src/test/java/it/projectAdministration/ProjectAdministrationTest.java +++ b/it/it-tests/src/test/java/it/projectAdministration/ProjectAdministrationTest.java @@ -23,6 +23,7 @@ import com.sonar.orchestrator.Orchestrator; import com.sonar.orchestrator.build.SonarScanner; import com.sonar.orchestrator.selenium.Selenese; import it.Category1Suite; +import java.io.UnsupportedEncodingException; import java.sql.SQLException; import java.util.Calendar; import java.util.GregorianCalendar; @@ -34,22 +35,27 @@ import org.junit.Test; import org.junit.rules.ExpectedException; import org.sonar.wsclient.SonarClient; import org.sonar.wsclient.base.HttpException; -import org.sonar.wsclient.services.PropertyQuery; import org.sonar.wsclient.services.ResourceQuery; import org.sonar.wsclient.user.UserParameters; +import pageobjects.Navigation; +import pageobjects.settings.SettingsPage; import util.selenium.SeleneseTest; import static org.assertj.core.api.Assertions.assertThat; import static util.ItUtils.projectDir; public class ProjectAdministrationTest { - private static final String DELETE_WS_ENDPOINT = "api/projects/bulk_delete"; + @ClassRule public static Orchestrator orchestrator = Category1Suite.ORCHESTRATOR; + @Rule public ExpectedException expectedException = ExpectedException.none(); + @Rule + public Navigation nav = Navigation.get(orchestrator); + private static final String PROJECT_KEY = "sample"; private static final String FILE_KEY = "sample:src/main/xoo/sample/Sample.xoo"; @@ -116,7 +122,7 @@ public class ProjectAdministrationTest { new SeleneseTest( Selenese.builder().setHtmlTestsInClasspath("project-deletion", "/projectAdministration/ProjectAdministrationTest/project-deletion/project-deletion.html").build()) - .runOn(orchestrator); + .runOn(orchestrator); } finally { wsClient.userClient().deactivate(projectAdminUser); } @@ -156,40 +162,37 @@ public class ProjectAdministrationTest { assertThat(count("events where category='Version'")).as("Different number of events").isEqualTo(1); } - /** - * SONAR-3425 - */ @Test - public void project_settings() { - scanSampleWithDate("2012-01-01"); - - Selenese selenese = Selenese.builder().setHtmlTestsInClasspath("project-settings", - // SONAR-3425 - "/projectAdministration/ProjectAdministrationTest/project-settings/override-global-settings.html", - - "/projectAdministration/ProjectAdministrationTest/project-settings/only-on-project-settings.html").build(); - new SeleneseTest(selenese).runOn(orchestrator); - - // GET /api/properties/sonar.exclusions?resource=sample - assertThat(orchestrator.getServer().getAdminWsClient().find(PropertyQuery.createForResource("sonar.exclusions", "sample")).getValue()) - .isEqualTo("my-exclusions"); - - // GET /api/properties?resource=sample - // Note that this WS is used by SonarLint - assertThat(orchestrator.getServer().getAdminWsClient().findAll(PropertyQuery.createForResource(null, "sample"))).isNotEmpty(); + public void display_project_settings() throws UnsupportedEncodingException { + scanSample(null, null); + + SettingsPage page = nav.logIn().asAdmin().openSettings("sample") + .assertMenuContains("Analysis Scope") + .assertMenuContains("Category 1") + .assertMenuContains("DEV") + .assertMenuContains("project-only") + .assertMenuContains("Xoo") + .assertSettingDisplayed("sonar.dbcleaner.daysBeforeDeletingClosedIssues"); + + page.openCategory("project-only") + .assertSettingDisplayed("prop_only_on_project"); + + page.openCategory("General") + .assertStringSettingValue("sonar.dbcleaner.daysBeforeDeletingClosedIssues", "30") + .assertStringSettingValue("sonar.timemachine.period1", "previous_version") + .assertBooleanSettingValue("sonar.dbcleaner.cleanDirectory", true) + .setStringValue("sonar.dbcleaner.daysBeforeDeletingClosedIssues", "1") + .assertStringSettingValue("sonar.dbcleaner.daysBeforeDeletingClosedIssues", "1"); } - /** - * SONAR-4060 - */ @Test - public void display_module_settings() { + public void display_module_settings() throws UnsupportedEncodingException { orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-multi-modules-sample"))); - Selenese selenese = Selenese.builder().setHtmlTestsInClasspath("module-settings", - // SONAR-3425 - "/projectAdministration/ProjectAdministrationTest/module-settings/display-module-settings.html").build(); - new SeleneseTest(selenese).runOn(orchestrator); + nav.logIn().asAdmin() + .openSettings("com.sonarsource.it.samples:multi-modules-sample:module_a") + .assertMenuContains("Analysis Scope") + .assertSettingDisplayed("sonar.coverage.exclusions"); } private void scanSampleWithDate(String date) { diff --git a/it/it-tests/src/test/java/it/settings/PropertySetsTest.java b/it/it-tests/src/test/java/it/settings/PropertySetsTest.java index fb4858b6ff4..0dca1cf94b5 100644 --- a/it/it-tests/src/test/java/it/settings/PropertySetsTest.java +++ b/it/it-tests/src/test/java/it/settings/PropertySetsTest.java @@ -20,20 +20,22 @@ package it.settings; import com.sonar.orchestrator.Orchestrator; -import com.sonar.orchestrator.selenium.Selenese; import it.Category1Suite; +import java.io.UnsupportedEncodingException; import java.util.List; import java.util.Map; import org.junit.After; import org.junit.BeforeClass; import org.junit.ClassRule; +import org.junit.Rule; import org.junit.Test; import org.sonarqube.ws.Settings; import org.sonarqube.ws.client.setting.ResetRequest; import org.sonarqube.ws.client.setting.SetRequest; import org.sonarqube.ws.client.setting.SettingsService; import org.sonarqube.ws.client.setting.ValuesRequest; -import util.selenium.SeleneseTest; +import pageobjects.Navigation; +import pageobjects.settings.SettingsPage; import static com.google.common.collect.Lists.newArrayList; import static java.util.Arrays.asList; @@ -47,6 +49,9 @@ public class PropertySetsTest { @ClassRule public static Orchestrator orchestrator = Category1Suite.ORCHESTRATOR; + @Rule + public Navigation nav = Navigation.get(orchestrator); + static SettingsService SETTINGS; @BeforeClass @@ -60,37 +65,37 @@ public class PropertySetsTest { } @Test - public void support_property_sets() { - Selenese selenese = Selenese.builder().setHtmlTestsInClasspath("property-sets", - "/settings/PropertySetsTest/property-sets/create.html", - "/settings/PropertySetsTest/property-sets/delete.html", - "/settings/PropertySetsTest/property-sets/reference.html", - "/settings/PropertySetsTest/property-sets/all_types.html").build(); - // Use the old runner because it fails with the new Selenium runner - orchestrator.executeSelenese(selenese); - - // SSF-25 Check that the password has well be setted as now it does not appears in the html source code - assertPropertySet("sonar.demo", - asList(entry("password", "abcde"), - entry("boolean", "true"), - entry("float", "42.0"), - entry("license", "abc"), - entry("list", "AAA"), - entry("metric", "overall_branch_coverage"), - entry("regexp", ".*"), - entry("text", "text"))); + public void support_property_sets() throws UnsupportedEncodingException { + SettingsPage page = nav.logIn().asAdmin().openSettings(null).openCategory("DEV") + .assertSettingDisplayed("sonar.test.jira.servers"); + + page.getPropertySetInput("sonar.test.jira.servers") + .setFieldValue("key", "jira1") + .setFieldValue("url", "http://jira") + .setFieldValue("port", "12345") + .save(); + + assertPropertySet("sonar.test.jira.servers", asList( + entry("key", "jira1"), + entry("url", "http://jira"), + entry("port", "12345"))); } @Test - public void support_property_sets_with_auto_generated_keys() { - new SeleneseTest( - Selenese.builder().setHtmlTestsInClasspath("create-auto-generated", - "/settings/PropertySetsTest/auto-generated/create.html").build()).runOn(orchestrator); - assertPropertySet("sonar.autogenerated", asList(entry("value", "FIRST")), asList(entry("value", "SECOND")), asList(entry("value", "THIRD"))); - - new SeleneseTest(Selenese.builder().setHtmlTestsInClasspath("update-auto-generated", - "/settings/PropertySetsTest/auto-generated/update.html").build()).runOn(orchestrator); - assertPropertySet("sonar.autogenerated", asList(entry("value", "FIRST")), asList(entry("value", "THIRD"))); + public void support_property_sets_with_auto_generated_keys() throws UnsupportedEncodingException { + SettingsPage page = nav.logIn().asAdmin().openSettings(null).openCategory("DEV") + .assertSettingDisplayed("sonar.autogenerated"); + + page.getPropertySetInput("sonar.autogenerated") + .setFieldValue(0, "value", "FIRST") + .setFieldValue(1, "value", "SECOND") + .setFieldValue(2, "value", "THIRD") + .save(); + + assertPropertySet("sonar.autogenerated", + asList(entry("value", "FIRST")), + asList(entry("value", "SECOND")), + asList(entry("value", "THIRD"))); } @Test @@ -124,7 +129,7 @@ public class PropertySetsTest { assertThat(setting.getFieldValues().getFieldValuesList()).hasSize(fieldsValues.length); int index = 0; for (Settings.FieldValues.Value fieldsValue : setting.getFieldValues().getFieldValuesList()) { - assertThat(fieldsValue.getValue()).containsOnly(fieldsValues[index].toArray(new Map.Entry[] {})); + assertThat(fieldsValue.getValue()).containsOnly(fieldsValues[index].toArray(new Map.Entry[]{})); index++; } } diff --git a/it/it-tests/src/test/java/it/settings/SettingsTestRestartingOrchestrator.java b/it/it-tests/src/test/java/it/settings/SettingsTestRestartingOrchestrator.java index 055c7991026..67b2eefa62f 100644 --- a/it/it-tests/src/test/java/it/settings/SettingsTestRestartingOrchestrator.java +++ b/it/it-tests/src/test/java/it/settings/SettingsTestRestartingOrchestrator.java @@ -22,11 +22,13 @@ package it.settings; import com.sonar.orchestrator.Orchestrator; import com.sonar.orchestrator.build.SonarScanner; import com.sonar.orchestrator.selenium.Selenese; +import java.io.UnsupportedEncodingException; import java.net.URL; import org.junit.After; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; +import pageobjects.Navigation; import util.selenium.SeleneseTest; import static util.ItUtils.pluginArtifact; @@ -51,7 +53,7 @@ public class SettingsTestRestartingOrchestrator { } @Test - public void test_settings() { + public void test_settings() throws UnsupportedEncodingException { URL secretKeyUrl = getClass().getResource("/settings/SettingsTest/sonar-secret.txt"); orchestrator = Orchestrator.builderEnv() .addPlugin(pluginArtifact("settings-plugin")) @@ -60,33 +62,28 @@ public class SettingsTestRestartingOrchestrator { .build(); orchestrator.start(); - Selenese selenese = Selenese.builder().setHtmlTestsInClasspath("test_settings", - "/settings/SettingsTest/general-settings.html", - - // SONAR-2869 the annotation @Properties can be used on extensions and not only on plugin entry points - "/settings/SettingsTest/hidden-extension-property.html", - "/settings/SettingsTest/global-extension-property.html", + Navigation.get(orchestrator).openHomepage().logIn().asAdmin().openSettings(null) + .assertMenuContains("General") + .assertSettingDisplayed("sonar.dbcleaner.cleanDirectory") + .assertSettingNotDisplayed("settings.extension.hidden") + .assertSettingNotDisplayed("settings.extension.global"); - // SONAR-3344 - licenses - "/settings/SettingsTest/ignore-corrupted-license.html", - "/settings/SettingsTest/display-license.html", - "/settings/SettingsTest/display-untyped-license.html", - - // SONAR-2084 - encryption + Selenese selenese = Selenese.builder().setHtmlTestsInClasspath("test_settings", + // test encryption "/settings/SettingsTest/generate-secret-key.html", - "/settings/SettingsTest/encrypt-text.html", - - // SONAR-1378 - property types - "/settings/SettingsTest/validate-property-type.html", - - // SONAR-3127 - hide passwords - "/settings/SettingsTest/hide-passwords.html" - ).build(); + "/settings/SettingsTest/encrypt-text.html" + + // test licenses + // TODO enable when license page will be rewritten + // "/settings/SettingsTest/ignore-corrupted-license.html", + // "/settings/SettingsTest/display-license.html", + // "/settings/SettingsTest/display-untyped-license.html" + ).build(); new SeleneseTest(selenese).runOn(orchestrator); } @Test - public void property_relocation() { + public void property_relocation() throws UnsupportedEncodingException { orchestrator = Orchestrator.builderEnv() .addPlugin(pluginArtifact("property-relocation-plugin")) .addPlugin(xooPlugin()) @@ -101,10 +98,10 @@ public class SettingsTestRestartingOrchestrator { // should not fail orchestrator.executeBuilds(withDeprecatedKey, withNewKey); - Selenese selenese = Selenese.builder().setHtmlTestsInClasspath("property_relocation", - "/settings/SettingsTest/property_relocation.html" - ).build(); - new SeleneseTest(selenese).runOn(orchestrator); + Navigation.get(orchestrator).openHomepage().logIn().asAdmin().openSettings(null) + .assertMenuContains("General") + .assertSettingDisplayed("sonar.newKey") + .assertSettingNotDisplayed("sonar.deprecatedKey"); } } diff --git a/it/it-tests/src/test/java/it/settings/SubCategoriesTest.java b/it/it-tests/src/test/java/it/settings/SubCategoriesTest.java deleted file mode 100644 index 5bca8b88d5d..00000000000 --- a/it/it-tests/src/test/java/it/settings/SubCategoriesTest.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * SonarQube - * Copyright (C) 2009-2016 SonarSource SA - * mailto:contact AT sonarsource DOT com - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package it.settings; - -import com.sonar.orchestrator.Orchestrator; -import com.sonar.orchestrator.build.SonarScanner; -import com.sonar.orchestrator.selenium.Selenese; -import it.Category1Suite; -import org.junit.ClassRule; -import org.junit.Test; -import org.sonar.wsclient.services.PropertyQuery; -import util.selenium.SeleneseTest; - -import static org.assertj.core.api.Assertions.assertThat; -import static util.ItUtils.projectDir; - -public class SubCategoriesTest { - - @ClassRule - public static Orchestrator orchestrator = Category1Suite.ORCHESTRATOR; - - /** - * SONAR-3159 - */ - @Test - public void should_support_global_subcategories() { - Selenese selenese = Selenese.builder().setHtmlTestsInClasspath("subcategories", - "/settings/subcategories/global-subcategories.html", - // SONAR-4495 - "/settings/subcategories/global-subcategories-no-default.html" - ).build(); - new SeleneseTest(selenese).runOn(orchestrator); - assertThat(getProperty("prop3", null)).isEqualTo("myValue"); - } - - /** - * SONAR-3159 - */ - @Test - public void should_support_project_subcategories() { - orchestrator.executeBuild(SonarScanner.create(projectDir("shared/xoo-sample"))); - - Selenese selenese = Selenese.builder().setHtmlTestsInClasspath("subcategories", - "/settings/subcategories/project-subcategories.html", - // SONAR-4495 - "/settings/subcategories/project-subcategories-no-default.html" - ).build(); - new SeleneseTest(selenese).runOn(orchestrator); - assertThat(getProperty("prop3", "sample")).isEqualTo("myValue2"); - } - - static String getProperty(String key, String resourceKeyOrId) { - return orchestrator.getServer().getAdminWsClient().find(new PropertyQuery().setKey(key).setResourceKeyOrId(resourceKeyOrId)).getValue(); - } -} diff --git a/it/it-tests/src/test/java/pageobjects/LoginPage.java b/it/it-tests/src/test/java/pageobjects/LoginPage.java index 2aea164f0cb..98e95b01df8 100644 --- a/it/it-tests/src/test/java/pageobjects/LoginPage.java +++ b/it/it-tests/src/test/java/pageobjects/LoginPage.java @@ -36,6 +36,10 @@ public class LoginPage { return submitCredentials(login, password, Navigation.class); } + public Navigation asAdmin() { + return submitCredentials("admin", "admin"); + } + public LoginPage submitWrongCredentials(String login, String password) { $("#login").val(login); $("#password").val(password); diff --git a/it/it-tests/src/test/java/pageobjects/Navigation.java b/it/it-tests/src/test/java/pageobjects/Navigation.java index ecd9cc6bc67..28a99e4b3f8 100644 --- a/it/it-tests/src/test/java/pageobjects/Navigation.java +++ b/it/it-tests/src/test/java/pageobjects/Navigation.java @@ -24,8 +24,12 @@ import com.codeborne.selenide.Selenide; import com.codeborne.selenide.SelenideElement; import com.codeborne.selenide.WebDriverRunner; import com.sonar.orchestrator.Orchestrator; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import javax.annotation.Nullable; import org.junit.rules.ExternalResource; import org.openqa.selenium.By; +import pageobjects.settings.SettingsPage; import static com.codeborne.selenide.Selenide.$; import static com.codeborne.selenide.Selenide.page; @@ -84,6 +88,13 @@ public class Navigation extends ExternalResource { return open("/background_tasks", BackgroundTasksPage.class); } + public SettingsPage openSettings(@Nullable String projectKey) throws UnsupportedEncodingException { + String url = projectKey != null ? + "/project/settings?id=" + URLEncoder.encode(projectKey, "UTF-8") : + "/settings"; + return open(url, SettingsPage.class); + } + public void open(String relativeUrl) { Selenide.open(relativeUrl); } diff --git a/it/it-tests/src/test/java/pageobjects/settings/PropertySetInput.java b/it/it-tests/src/test/java/pageobjects/settings/PropertySetInput.java new file mode 100644 index 00000000000..9da8501fc02 --- /dev/null +++ b/it/it-tests/src/test/java/pageobjects/settings/PropertySetInput.java @@ -0,0 +1,48 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package pageobjects.settings; + +import com.codeborne.selenide.SelenideElement; + +import static com.codeborne.selenide.Condition.exist; + +public class PropertySetInput { + + private final SelenideElement elt; + + public PropertySetInput(SelenideElement elt) { + this.elt = elt; + } + + public PropertySetInput setFieldValue(int index, String fieldKey, String value) { + elt.findAll("input[name$=\"[" + fieldKey + "]\"]").get(index).val(value); + return this; + } + + public PropertySetInput setFieldValue(String fieldKey, String value) { + return setFieldValue(0, fieldKey, value); + } + + public PropertySetInput save() { + elt.find(".js-save-changes").click(); + elt.find(".js-save-changes").shouldNot(exist); + return this; + } +} diff --git a/it/it-tests/src/test/java/pageobjects/settings/SettingsPage.java b/it/it-tests/src/test/java/pageobjects/settings/SettingsPage.java new file mode 100644 index 00000000000..24676e896e5 --- /dev/null +++ b/it/it-tests/src/test/java/pageobjects/settings/SettingsPage.java @@ -0,0 +1,84 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package pageobjects.settings; + +import com.codeborne.selenide.SelenideElement; +import org.openqa.selenium.By; + +import static com.codeborne.selenide.Condition.cssClass; +import static com.codeborne.selenide.Condition.exactValue; +import static com.codeborne.selenide.Condition.exist; +import static com.codeborne.selenide.Condition.visible; +import static com.codeborne.selenide.Selenide.$; + +public class SettingsPage { + + public SettingsPage() { + $("#settings-page").shouldBe(visible); + } + + public SettingsPage assertMenuContains(String categoryName) { + $(".settings-menu").$(By.linkText(categoryName)).shouldBe(visible); + return this; + } + + public SettingsPage assertSettingDisplayed(String settingKey) { + $(".settings-definition[data-key='" + settingKey + "']").shouldBe(visible); + return this; + } + + public SettingsPage assertSettingNotDisplayed(String settingKey) { + $(".settings-definition[data-key='" + settingKey + "']").shouldNotBe(visible); + return this; + } + + public SettingsPage openCategory(String categoryName) { + $(".settings-menu").$(By.linkText(categoryName)).click(); + return this; + } + + public SettingsPage assertStringSettingValue(String settingKey, String value) { + $("input[name=\"settings[" + settingKey + "]\"]").shouldHave(exactValue(value)); + return this; + } + + public SettingsPage assertBooleanSettingValue(String settingKey, boolean value) { + SelenideElement toggle = $("button[name=\"settings[" + settingKey + "]\"]"); + if (value) { + toggle.shouldHave(cssClass("boolean-toggle-on")); + } else { + toggle.shouldNotHave(cssClass("boolean-toggle-on")); + } + return this; + } + + public SettingsPage setStringValue(String settingKey, String value) { + SelenideElement setting = $(".settings-definition[data-key=\"" + settingKey + "\"]"); + setting.find("input").val(value); + setting.find(".js-save-changes").click(); + setting.find(".js-save-changes").shouldNot(exist); + return this; + } + + public PropertySetInput getPropertySetInput(String settingKey) { + SelenideElement setting = $(".settings-definition[data-key=\"" + settingKey + "\"]"); + return new PropertySetInput(setting); + } +} diff --git a/it/it-tests/src/test/resources/componentDashboard/DashboardTest/global_dashboard/global-admin-dashboards.html b/it/it-tests/src/test/resources/componentDashboard/DashboardTest/global_dashboard/global-admin-dashboards.html deleted file mode 100644 index 67911a11d8c..00000000000 --- a/it/it-tests/src/test/resources/componentDashboard/DashboardTest/global_dashboard/global-admin-dashboards.html +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - global-admin-dashboards - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
global-admin-dashboards
open/sessions/login
typeloginadmin
typepasswordadmin
clickAndWaitcommit
open/
waitForElementPresentlink=Home
clickAndWaitlink=Home
clickAndWaitlink=Configure widgets
clickAndWaitlink=Back to dashboard
open/dashboards
open/settings/index
waitForElementPresentlink=Configuration
clicklink=Configuration
clickAndWaitlink=Default Dashboards
- - diff --git a/it/it-tests/src/test/resources/measureHistory/DifferentialPeriodsTest/define-leak-period-on-project.html b/it/it-tests/src/test/resources/measureHistory/DifferentialPeriodsTest/define-leak-period-on-project.html deleted file mode 100644 index 0bba5f6e3b3..00000000000 --- a/it/it-tests/src/test/resources/measureHistory/DifferentialPeriodsTest/define-leak-period-on-project.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - display-added-files - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
should_display_added_files_in_differential_drilldown
open/sessions/logout
open/project/settings?id=sample&category=general&subcategory=differentialviews
typeid=loginadmin
typeid=passwordadmin
clickAndWaitname=commit
waitForTextproperties*Leak Period*
- - diff --git a/it/it-tests/src/test/resources/projectAdministration/ProjectAdministrationTest/module-settings/display-module-settings.html b/it/it-tests/src/test/resources/projectAdministration/ProjectAdministrationTest/module-settings/display-module-settings.html deleted file mode 100644 index 6d301d77dbd..00000000000 --- a/it/it-tests/src/test/resources/projectAdministration/ProjectAdministrationTest/module-settings/display-module-settings.html +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - override-global-settings - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
override-global-settings
open/sessions/logout
open/project/settings/com.sonarsource.it.samples%3Amulti-modules-sample%3Amodule_a
typeid=loginadmin
typeid=passwordadmin
clickAndWaitname=commit
waitForTextplugins*Settings*
- - diff --git a/it/it-tests/src/test/resources/projectAdministration/ProjectAdministrationTest/project-settings/only-on-project-settings.html b/it/it-tests/src/test/resources/projectAdministration/ProjectAdministrationTest/project-settings/only-on-project-settings.html deleted file mode 100644 index 9197fa9c348..00000000000 --- a/it/it-tests/src/test/resources/projectAdministration/ProjectAdministrationTest/project-settings/only-on-project-settings.html +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - override-global-settings - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
override-global-settings
open/sessions/logout
open/project/settings/sample?category=project-only
typeid=loginadmin
typeid=passwordadmin
clickAndWaitname=commit
waitForElementPresentcss=.js-user-authenticated
typeid=input_prop_only_on_projectfoo
clickid=submit_settings
waitForValueid=input_prop_only_on_projectfoo
- - diff --git a/it/it-tests/src/test/resources/projectAdministration/ProjectAdministrationTest/project-settings/override-global-settings.html b/it/it-tests/src/test/resources/projectAdministration/ProjectAdministrationTest/project-settings/override-global-settings.html deleted file mode 100644 index a73b2ca1475..00000000000 --- a/it/it-tests/src/test/resources/projectAdministration/ProjectAdministrationTest/project-settings/override-global-settings.html +++ /dev/null @@ -1,64 +0,0 @@ - - - - - - override-global-settings - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
override-global-settings
open/sessions/logout
open/project/settings/sample?category=exclusions&subcategory=files
typeid=loginadmin
typeid=passwordadmin
clickAndWaitname=commit
waitForElementPresentcss=.js-user-authenticated
typeid=input_sonar.exclusionsmy-exclusions
clickid=submit_settings
waitForValueid=input_sonar.exclusionsmy-exclusions
- - diff --git a/it/it-tests/src/test/resources/serverSystem/ServerSystemTest/missing_ip.html b/it/it-tests/src/test/resources/serverSystem/ServerSystemTest/missing_ip.html index 1a9725f07a8..82e481ae7b9 100644 --- a/it/it-tests/src/test/resources/serverSystem/ServerSystemTest/missing_ip.html +++ b/it/it-tests/src/test/resources/serverSystem/ServerSystemTest/missing_ip.html @@ -60,22 +60,7 @@ open - /settings/index - - - - clickAndWait - link=Licenses - - - - clickAndWait - link=Server ID - - - - selectFrame - settings_iframe + /server_id_configuration diff --git a/it/it-tests/src/test/resources/serverSystem/ServerSystemTest/organisation_must_not_accept_special_chars.html b/it/it-tests/src/test/resources/serverSystem/ServerSystemTest/organisation_must_not_accept_special_chars.html index 29823121244..d9b98cf4ed7 100644 --- a/it/it-tests/src/test/resources/serverSystem/ServerSystemTest/organisation_must_not_accept_special_chars.html +++ b/it/it-tests/src/test/resources/serverSystem/ServerSystemTest/organisation_must_not_accept_special_chars.html @@ -35,22 +35,7 @@ open - /settings/index - - - - clickAndWait - link=Licenses - - - - clickAndWait - link=Server ID - - - - selectFrame - settings_iframe + /server_id_configuration diff --git a/it/it-tests/src/test/resources/serverSystem/ServerSystemTest/valid_id.html b/it/it-tests/src/test/resources/serverSystem/ServerSystemTest/valid_id.html index 4f86397d642..dabdb002dff 100644 --- a/it/it-tests/src/test/resources/serverSystem/ServerSystemTest/valid_id.html +++ b/it/it-tests/src/test/resources/serverSystem/ServerSystemTest/valid_id.html @@ -40,27 +40,7 @@ open - /settings/index - - - - clickAndWait - link=Licenses - - - - clickAndWait - link=Server ID - - - - waitForElementPresent - settings_iframe - - - - selectFrame - settings_iframe + /server_id_configuration diff --git a/it/it-tests/src/test/resources/settings/PropertySetsTest/auto-generated/create.html b/it/it-tests/src/test/resources/settings/PropertySetsTest/auto-generated/create.html deleted file mode 100644 index a28e6426fdf..00000000000 --- a/it/it-tests/src/test/resources/settings/PropertySetsTest/auto-generated/create.html +++ /dev/null @@ -1,84 +0,0 @@ - - - - - - create - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
open/sessions/new
typeloginadmin
typepasswordadmin
clickAndWaitcommit
waitForElementPresentcss=.js-user-authenticated
open/settings?category=DEV
waitForValuename=page_version1
typeid=input_valueFIRST
clickcss=button.add_value
typexpath=(//input[@id='input_value'])[2]SECOND
clickcss=button.add_value
typexpath=(//input[@id='input_value'])[3]THIRD
clickid=submit_settings
waitForValuename=page_version2
- - diff --git a/it/it-tests/src/test/resources/settings/PropertySetsTest/auto-generated/update.html b/it/it-tests/src/test/resources/settings/PropertySetsTest/auto-generated/update.html deleted file mode 100644 index 2db17093913..00000000000 --- a/it/it-tests/src/test/resources/settings/PropertySetsTest/auto-generated/update.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - update - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
open/sessions/new
typeloginadmin
typepasswordadmin
clickAndWaitcommit
waitForElementPresentcss=.js-user-authenticated
open/settings?category=DEV
waitForValuename=page_version1
clicklink=Delete
waitForVisibleid=submit_settings
clickid=submit_settings
waitForValuename=page_version2
- - diff --git a/it/it-tests/src/test/resources/settings/PropertySetsTest/property-sets/all_types.html b/it/it-tests/src/test/resources/settings/PropertySetsTest/property-sets/all_types.html deleted file mode 100644 index 4dfffa1d384..00000000000 --- a/it/it-tests/src/test/resources/settings/PropertySetsTest/property-sets/all_types.html +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - all_types - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
open/sessions/new
typeloginadmin
typepasswordadmin
clickAndWaitcommit
waitForElementPresentcss=.js-user-authenticated
open/settings?category=DEV
waitForValuename=page_version1
typeid=input_texttext
selectid=input_booleanlabel=True
typeid=input_float42.0
typeid=input_licenseabc
selectid=input_metriclabel=Overall Condition Coverage
typeid=input_passwordabcde
typeid=input_regexp.*
selectid=input_listlabel=AAA
clickid=submit_settings
waitForValuename=page_version2
assertValueid=input_texttext
assertValueid=input_booleantrue
assertValueid=input_float42.0
assertValueid=input_licenseabc
assertValueid=input_metricoverall_branch_coverage
assertValueid=input_password{{*******************}}
assertValueid=input_regexpexact:.*
assertValueid=input_listAAA
- - diff --git a/it/it-tests/src/test/resources/settings/PropertySetsTest/property-sets/create.html b/it/it-tests/src/test/resources/settings/PropertySetsTest/property-sets/create.html deleted file mode 100644 index d85e447c5c4..00000000000 --- a/it/it-tests/src/test/resources/settings/PropertySetsTest/property-sets/create.html +++ /dev/null @@ -1,89 +0,0 @@ - - - - - - create - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
open/sessions/new
typeloginadmin
typepasswordadmin
clickAndWaitcommit
waitForElementPresentcss=.js-user-authenticated
open/settings?category=DEV
waitForValuename=page_version1
typeid=input_keyjira1
typeid=input_urlhttp://jira
typeid=input_port12345
clickid=submit_settings
waitForValuename=page_version2
assertValueid=input_keyjira1
assertValueid=input_urlexact:http://jira
assertValueid=input_port12345
- - diff --git a/it/it-tests/src/test/resources/settings/PropertySetsTest/property-sets/delete.html b/it/it-tests/src/test/resources/settings/PropertySetsTest/property-sets/delete.html deleted file mode 100644 index aa0e9cacc6d..00000000000 --- a/it/it-tests/src/test/resources/settings/PropertySetsTest/property-sets/delete.html +++ /dev/null @@ -1,119 +0,0 @@ - - - - - - delete - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
open/sessions/new
typeloginadmin
typepasswordadmin
clickAndWaitcommit
waitForElementPresentcss=.js-user-authenticated
open/settings?category=DEV
waitForValuename=page_version1
typeid=input_keyjira1
typeid=input_urlhttp://jira1
clickcss=#block_sonar\.test\.jira\.servers .add_value
typexpath=(//input[@id='input_key'])[2]jira2
typexpath=(//input[@id='input_url'])[2]http://jira2
clickid=submit_settings
waitForValuename=page_version2
assertValuexpath=(//input[@id='input_key'])[1]jira1
assertValuexpath=(//input[@id='input_key'])[2]jira2
clickxpath=(//a[contains(text(),'Delete')])[3]
clickid=submit_settings
waitForValuename=page_version3
assertValuexpath=(//input[@id='input_key'])[1]jira1
waitForNotTextxpath=(//input[@id='input_key'])*jira2*
- - diff --git a/it/it-tests/src/test/resources/settings/PropertySetsTest/property-sets/reference.html b/it/it-tests/src/test/resources/settings/PropertySetsTest/property-sets/reference.html deleted file mode 100644 index c212340711f..00000000000 --- a/it/it-tests/src/test/resources/settings/PropertySetsTest/property-sets/reference.html +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - reference - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
open/sessions/new
typeloginadmin
typepasswordadmin
clickAndWaitcommit
waitForElementPresentcss=.js-user-authenticated
open/settings?category=DEV
waitForValuename=page_version1
typexpath=(//input[@id='input_key'])[1]jira1
clickcss=#block_sonar\.test\.jira\.servers .add_value
typexpath=(//input[@id='input_key'])[2]jira2
clickid=submit_settings
waitForValuename=page_version2
assertSelectOptionsid=input_sonar.test.jiraDefault,jira1,jira2
clickxpath=(//a[contains(text(),'Delete')])[2]
clickid=submit_settings
waitForValuename=page_version3
- - diff --git a/it/it-tests/src/test/resources/settings/SettingsTest/encrypt-text.html b/it/it-tests/src/test/resources/settings/SettingsTest/encrypt-text.html index fe174478d54..d1258d789ac 100644 --- a/it/it-tests/src/test/resources/settings/SettingsTest/encrypt-text.html +++ b/it/it-tests/src/test/resources/settings/SettingsTest/encrypt-text.html @@ -15,7 +15,7 @@ open - /settings?category=security&subcategory=encryption + /encryption_configuration @@ -35,12 +35,7 @@ waitForElementPresent - css=.js-user-authenticated - - - - selectFrame - settings_iframe + css=#submit_encrypt diff --git a/it/it-tests/src/test/resources/settings/SettingsTest/general-settings.html b/it/it-tests/src/test/resources/settings/SettingsTest/general-settings.html deleted file mode 100644 index 89061e138ba..00000000000 --- a/it/it-tests/src/test/resources/settings/SettingsTest/general-settings.html +++ /dev/null @@ -1,59 +0,0 @@ - - - - - - settings_on_core_plugins - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
settings_on_core_plugins
open/sessions/logout
open/sessions/new
typeloginadmin
typepasswordadmin
clickAndWaitcommit
waitForElementPresentcss=.js-user-authenticated
open/settings/index
waitForTextplugins*General*
- - diff --git a/it/it-tests/src/test/resources/settings/SettingsTest/generate-secret-key.html b/it/it-tests/src/test/resources/settings/SettingsTest/generate-secret-key.html index c667bf3ca0b..dafe8ccfe82 100644 --- a/it/it-tests/src/test/resources/settings/SettingsTest/generate-secret-key.html +++ b/it/it-tests/src/test/resources/settings/SettingsTest/generate-secret-key.html @@ -15,7 +15,7 @@ open - /settings?category=security&subcategory=encryption + /encryption_configuration @@ -35,12 +35,7 @@ waitForElementPresent - css=.js-user-authenticated - - - - selectFrame - settings_iframe + css=#submit_encrypt diff --git a/it/it-tests/src/test/resources/settings/SettingsTest/global-extension-property.html b/it/it-tests/src/test/resources/settings/SettingsTest/global-extension-property.html deleted file mode 100644 index ad3d97682b3..00000000000 --- a/it/it-tests/src/test/resources/settings/SettingsTest/global-extension-property.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - global-extension-property - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
global-extension-property
open/sessions/new
typeloginadmin
typepasswordadmin
clickAndWaitcommit
waitForElementPresentcss=.js-user-authenticated
open/settings?category=Settings
assertNotTextpluginsglob:*Hidden*
- - diff --git a/it/it-tests/src/test/resources/settings/SettingsTest/hidden-extension-property.html b/it/it-tests/src/test/resources/settings/SettingsTest/hidden-extension-property.html deleted file mode 100644 index 7aa9bd9cba1..00000000000 --- a/it/it-tests/src/test/resources/settings/SettingsTest/hidden-extension-property.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - hidden-extension-property - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
hidden-extension-property
open/sessions/new
typeloginadmin
typepasswordadmin
clickAndWaitcommit
waitForElementPresentcss=.js-user-authenticated
open/settings?category=Settings
assertNotTextpluginsglob:*Hidden*
- - diff --git a/it/it-tests/src/test/resources/settings/SettingsTest/hide-passwords.html b/it/it-tests/src/test/resources/settings/SettingsTest/hide-passwords.html deleted file mode 100644 index 531e11e8bd4..00000000000 --- a/it/it-tests/src/test/resources/settings/SettingsTest/hide-passwords.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - hide-passwords - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
open/sessions/logout
open/settings?category=Settings
typeid=loginadmin
typeid=passwordadmin
clickAndWaitname=commit
waitForElementPresentcss=.js-user-authenticated
waitForTextblock_password*Default*
assertNotTextblock_password*Default*sonar*
- - diff --git a/it/it-tests/src/test/resources/settings/SettingsTest/property_relocation.html b/it/it-tests/src/test/resources/settings/SettingsTest/property_relocation.html deleted file mode 100644 index 37986cd560d..00000000000 --- a/it/it-tests/src/test/resources/settings/SettingsTest/property_relocation.html +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - property-relocation - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
open/sessions/logout
open/settings/index?category=general
typeid=loginadmin
typeid=passwordadmin
clickAndWaitname=commit
waitForElementPresentcss=.js-user-authenticated
waitForTextproperties*sonar.newKey*
assertNotTextproperties*sonar.deprecatedKey*
- - diff --git a/it/it-tests/src/test/resources/settings/SettingsTest/validate-property-type.html b/it/it-tests/src/test/resources/settings/SettingsTest/validate-property-type.html deleted file mode 100644 index 96024f79b91..00000000000 --- a/it/it-tests/src/test/resources/settings/SettingsTest/validate-property-type.html +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - validate-property-type - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
open/sessions/logout
open/settings?category=Settings
typeid=loginadmin
typeid=passwordadmin
clickAndWaitname=commit
waitForElementPresentcss=.js-user-authenticated
typeid=input_floatabc
typeid=input_integer123
clickid=submit_settings
waitForTextproperties*Not a floating point number*
assertValueinput_integer123
open/settings?category=Settings
assertValueinput_float
- - diff --git a/it/it-tests/src/test/resources/settings/subcategories/global-subcategories-no-default.html b/it/it-tests/src/test/resources/settings/subcategories/global-subcategories-no-default.html deleted file mode 100644 index a4b69712397..00000000000 --- a/it/it-tests/src/test/resources/settings/subcategories/global-subcategories-no-default.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - global-subcategories - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
open/sessions/new
typeloginadmin
typepasswordadmin
clickAndWaitcommit
waitForElementPresentcss=.js-user-authenticated
open/settings?category=Category 2
waitForValuename=page_version1
assertElementPresentid=input_prop2_1
assertElementNotPresentid=input_prop2_2
- - diff --git a/it/it-tests/src/test/resources/settings/subcategories/global-subcategories.html b/it/it-tests/src/test/resources/settings/subcategories/global-subcategories.html deleted file mode 100644 index 3f578222762..00000000000 --- a/it/it-tests/src/test/resources/settings/subcategories/global-subcategories.html +++ /dev/null @@ -1,156 +0,0 @@ - - - - - - global-subcategories - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
open/sessions/new
typeloginadmin
typepasswordadmin
clickAndWaitcommit
waitForElementPresentcss=.js-user-authenticated
open/settings?category=Category 1
waitForValuename=page_version1
assertElementNotPresentid=input_prop1
assertElementNotPresentid=input_prop2
assertElementNotPresentid=input_prop3
assertElementPresentid=input_prop4
clickAndWaitlink=Sub category 1
assertElementPresentid=input_prop1
assertElementPresentid=input_prop2
assertElementNotPresentid=input_prop3
assertElementNotPresentid=input_prop4
assertElementPresentxpath=//.[@id='input_prop2']/following::input[@id='input_prop1']
clickAndWaitlink=Sub category 2
typeid=input_prop3myValue
clickid=submit_settings
waitForValuename=page_version2
assertElementNotPresentid=input_prop1
assertElementNotPresentid=input_prop2
assertElementPresentid=input_prop3
assertElementNotPresentid=input_prop4
assertValueid=input_prop3myValue
clickAndWaitlink=Sub category 1
assertElementPresentid=input_prop1
- - diff --git a/it/it-tests/src/test/resources/settings/subcategories/project-subcategories-no-default.html b/it/it-tests/src/test/resources/settings/subcategories/project-subcategories-no-default.html deleted file mode 100644 index f0efe3fcf35..00000000000 --- a/it/it-tests/src/test/resources/settings/subcategories/project-subcategories-no-default.html +++ /dev/null @@ -1,60 +0,0 @@ - - - - - - create - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
open/sessions/new
typeloginadmin
typepasswordadmin
clickAndWaitcommit
waitForElementPresentcss=.js-user-authenticated
open/project/settings/sample?category=Category 2
waitForValuename=page_version1
assertElementPresentid=input_prop2_1
assertElementNotPresentid=input_prop2_2
- - diff --git a/it/it-tests/src/test/resources/settings/subcategories/project-subcategories.html b/it/it-tests/src/test/resources/settings/subcategories/project-subcategories.html deleted file mode 100644 index 1d6b7efc0c7..00000000000 --- a/it/it-tests/src/test/resources/settings/subcategories/project-subcategories.html +++ /dev/null @@ -1,150 +0,0 @@ - - - - - - create - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
open/sessions/new
typeloginadmin
typepasswordadmin
clickAndWaitcommit
waitForElementPresentcss=.js-user-authenticated
open/project/settings/sample?category=Category 1
waitForValuename=page_version1
assertElementNotPresentid=input_prop1
assertElementNotPresentid=input_prop2
assertElementNotPresentid=input_prop3
assertElementPresentid=input_prop4
clickAndWaitlink=Sub category 1
assertElementPresentid=input_prop1
assertElementPresentid=input_prop2
assertElementNotPresentid=input_prop3
assertElementNotPresentid=input_prop4
clickAndWaitlink=Sub category 2
typeid=input_prop3myValue2
clickid=submit_settings
waitForValuename=page_version2
assertElementNotPresentid=input_prop1
assertElementNotPresentid=input_prop2
assertElementPresentid=input_prop3
assertElementNotPresentid=input_prop4
assertValueid=input_prop3myValue2
clickAndWaitlink=Sub category 1
assertElementPresentid=input_prop1
- - diff --git a/it/it-tests/src/test/resources/updateCenter/installed-plugins.html b/it/it-tests/src/test/resources/updateCenter/installed-plugins.html index 05c21066451..01c518c4f9f 100644 --- a/it/it-tests/src/test/resources/updateCenter/installed-plugins.html +++ b/it/it-tests/src/test/resources/updateCenter/installed-plugins.html @@ -15,7 +15,7 @@ open - /settings + /updatecenter @@ -38,11 +38,6 @@ css=.js-user-authenticated - - open - /updatecenter - - waitForText content diff --git a/it/it-tests/src/test/resources/user/LocalAuthenticationTest/redirect_to_original_url_after_indirect_login.html b/it/it-tests/src/test/resources/user/LocalAuthenticationTest/redirect_to_original_url_after_indirect_login.html index ef3618524db..9683c351923 100644 --- a/it/it-tests/src/test/resources/user/LocalAuthenticationTest/redirect_to_original_url_after_indirect_login.html +++ b/it/it-tests/src/test/resources/user/LocalAuthenticationTest/redirect_to_original_url_after_indirect_login.html @@ -14,7 +14,7 @@ open - /settings/index + /settings @@ -54,12 +54,7 @@ assertLocation - */settings/index - - - - assertElementPresent - plugins + */settings diff --git a/server/sonar-web/config/webpack/webpack.config.base.js b/server/sonar-web/config/webpack/webpack.config.base.js index 0533884f245..2e0dcc48236 100644 --- a/server/sonar-web/config/webpack/webpack.config.base.js +++ b/server/sonar-web/config/webpack/webpack.config.base.js @@ -45,6 +45,7 @@ module.exports = { 'projects': './src/main/js/apps/projects/app.js', 'quality-gates': './src/main/js/apps/quality-gates/app.js', 'quality-profiles': './src/main/js/apps/quality-profiles/app.js', + 'settings': './src/main/js/apps/settings/app.js', 'source-viewer': './src/main/js/apps/source-viewer/app.js', 'system': './src/main/js/apps/system/app.js', 'update-center': './src/main/js/apps/update-center/app.js', diff --git a/server/sonar-web/scripts/start.js b/server/sonar-web/scripts/start.js index ceb9d61928a..905793318e2 100644 --- a/server/sonar-web/scripts/start.js +++ b/server/sonar-web/scripts/start.js @@ -121,14 +121,10 @@ function setupCompiler () { function runDevServer (port) { app.use(require('webpack-dev-middleware')(compiler, { noInfo: true, - quiet: true, publicPath: config.output.publicPath })); - app.use(require('webpack-hot-middleware')(compiler, { - noInfo: true, - quiet: true - })); + app.use(require('webpack-hot-middleware')(compiler)); app.all('*', proxy(API_HOST, { forwardPath: function (req) { diff --git a/server/sonar-web/src/main/js/api/settings.js b/server/sonar-web/src/main/js/api/settings.js new file mode 100644 index 00000000000..14396243a2f --- /dev/null +++ b/server/sonar-web/src/main/js/api/settings.js @@ -0,0 +1,69 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import omitBy from 'lodash/omitBy'; +import { getJSON, post } from '../helpers/request'; +import { TYPE_PROPERTY_SET } from '../apps/settings/constants'; + +export function getDefinitions (componentKey) { + const url = '/api/settings/list_definitions'; + const data = componentKey ? { componentKey } : {}; + return getJSON(url, data).then(r => r.definitions); +} + +export function getValues (keys, componentKey) { + const url = '/api/settings/values'; + const data = { keys }; + if (componentKey) { + data.componentKey = componentKey; + } + return getJSON(url, data).then(r => r.settings); +} + +export function setSettingValue (definition, value, componentKey) { + const url = '/api/settings/set'; + + const { key } = definition; + const data = { key }; + + if (definition.multiValues) { + data.values = value; + } else if (definition.type === TYPE_PROPERTY_SET) { + data.fieldValues = value + .map(fields => omitBy(fields, value => value == null)) + .map(JSON.stringify); + } else { + data.value = value; + } + + if (componentKey) { + data.componentKey = componentKey; + } + + return post(url, data); +} + +export function resetSettingValue (key, componentKey) { + const url = '/api/settings/reset'; + const data = { key }; + if (componentKey) { + data.componentKey = componentKey; + } + return post(url, data); +} diff --git a/server/sonar-web/src/main/js/api/users.js b/server/sonar-web/src/main/js/api/users.js index 80865674d80..e5d5d15f7bc 100644 --- a/server/sonar-web/src/main/js/api/users.js +++ b/server/sonar-web/src/main/js/api/users.js @@ -39,3 +39,9 @@ export function getIdentityProviders () { const url = '/api/users/identity_providers'; return getJSON(url); } + +export function searchUsers (query) { + const url = '/api/users/search'; + const data = { q: query }; + return getJSON(url, data); +} diff --git a/server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.js b/server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.js new file mode 100644 index 00000000000..16b709d99ab --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/__tests__/utils-test.js @@ -0,0 +1,46 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { expect } from 'chai'; +import { getEmptyValue } from '../utils'; +import { TYPE_PROPERTY_SET, TYPE_STRING, TYPE_SINGLE_SELECT_LIST, TYPE_BOOLEAN } from '../constants'; + +const fields = [ + { key: 'foo', type: TYPE_STRING }, + { key: 'bar', type: TYPE_SINGLE_SELECT_LIST } +]; + +describe('Settings :: Utils', () => { + describe('#getEmptyValue()', () => { + it('should work for property sets', () => { + const setting = { type: TYPE_PROPERTY_SET, fields }; + expect(getEmptyValue(setting)).to.deep.equal([{ foo: '', bar: null }]); + }); + + it('should work for multi values string', () => { + const setting = { type: TYPE_STRING, multiValues: true }; + expect(getEmptyValue(setting)).to.deep.equal(['']); + }); + + it('should work for multi values boolean', () => { + const setting = { type: TYPE_BOOLEAN, multiValues: true }; + expect(getEmptyValue(setting)).to.deep.equal([null]); + }); + }); +}); diff --git a/server/sonar-web/src/main/js/apps/settings/app.js b/server/sonar-web/src/main/js/apps/settings/app.js new file mode 100644 index 00000000000..cf9dfcf07ad --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/app.js @@ -0,0 +1,50 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import { render } from 'react-dom'; +import { Provider } from 'react-redux'; +import { Router, Route, Redirect, useRouterHistory } from 'react-router'; +import { createHistory } from 'history'; +import App from './components/App'; +import rootReducer from './store/rootReducer'; +import configureStore from '../../components/store/configureStore'; + +window.sonarqube.appStarted.then(options => { + const el = document.querySelector(options.el); + + const controller = options.component ? '/project/settings' : '/settings'; + const history = useRouterHistory(createHistory)({ + basename: window.baseUrl + controller + }); + + const store = configureStore(rootReducer); + + const withComponent = ComposedComponent => props => + ; + + render(( + + + + + + + ), el); +}); diff --git a/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.js b/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.js new file mode 100644 index 00000000000..44d16ad6bdc --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/AllCategoriesList.js @@ -0,0 +1,37 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import { connect } from 'react-redux'; +import CategoriesList from './CategoriesList'; +import { getAllCategories } from '../store/rootReducer'; + +class AllCategoriesList extends React.Component { + render () { + return ; + } +} + +const mapStateToProps = state => ({ + categories: getAllCategories(state) +}); + +export default connect( + mapStateToProps +)(AllCategoriesList); diff --git a/server/sonar-web/src/main/js/apps/settings/components/App.js b/server/sonar-web/src/main/js/apps/settings/components/App.js new file mode 100644 index 00000000000..424675ba502 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/App.js @@ -0,0 +1,101 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import shallowCompare from 'react-addons-shallow-compare'; +import { connect } from 'react-redux'; +import PageHeader from './PageHeader'; +import CategoryDefinitionsList from './CategoryDefinitionsList'; +import AllCategoriesList from './AllCategoriesList'; +import GlobalMessagesContainer from './GlobalMessagesContainer'; +import { fetchSettings } from '../store/actions'; +import { getDefaultCategory } from '../store/rootReducer'; +import '../styles.css'; + +class App extends React.Component { + static propTypes = { + component: React.PropTypes.object, + fetchSettings: React.PropTypes.func.isRequired, + defaultCategory: React.PropTypes.string + }; + + state = { loaded: false }; + + componentDidMount () { + document.querySelector('html').classList.add('dashboard-page'); + const componentKey = this.props.component ? this.props.component.key : null; + this.props.fetchSettings(componentKey).then(() => { + this.setState({ loaded: true }); + }); + } + + shouldComponentUpdate (nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } + + componentDidUpdate (prevProps) { + if (prevProps.component !== this.props.component) { + const componentKey = this.props.component ? this.props.component.key : null; + this.props.fetchSettings(componentKey); + } + } + + componentWillUnmount () { + document.querySelector('html').classList.remove('dashboard-page'); + } + + render () { + if (!this.state.loaded) { + return null; + } + + const { query } = this.props.location; + const selectedCategory = query.category || this.props.defaultCategory; + + return ( +
+ + +
+
+ +
+
+ +
+
+
+ ); + } +} + +const mapStateToProps = state => ({ + defaultCategory: getDefaultCategory(state) +}); + +export default connect( + mapStateToProps, + { fetchSettings } +)(App); + diff --git a/server/sonar-web/src/main/js/apps/settings/components/CategoriesList.js b/server/sonar-web/src/main/js/apps/settings/components/CategoriesList.js new file mode 100644 index 00000000000..aa833d5ce90 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/CategoriesList.js @@ -0,0 +1,71 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import shallowCompare from 'react-addons-shallow-compare'; +import sortBy from 'lodash/sortBy'; +import { IndexLink } from 'react-router'; +import { getCategoryName } from '../utils'; + +export default class CategoriesList extends React.Component { + static propTypes = { + categories: React.PropTypes.array.isRequired, + selectedCategory: React.PropTypes.string.isRequired, + defaultCategory: React.PropTypes.string.isRequired + }; + + shouldComponentUpdate (nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } + + renderLink (category) { + const query = {}; + + if (category.key !== this.props.defaultCategory) { + query.category = category.key.toLowerCase(); + } + + if (this.props.component) { + query.id = this.props.component.key; + } + + const className = category.key.toLowerCase() === this.props.selectedCategory.toLowerCase() ? 'active' : ''; + + return ( + + {category.name} + + ); + } + + render () { + const categoriesWithName = this.props.categories.map(key => ({ key, name: getCategoryName(key) })); + const sortedCategories = sortBy(categoriesWithName, category => category.name.toLowerCase()); + + return ( +
    + {sortedCategories.map(category => ( +
  • + {this.renderLink(category)} +
  • + ))} +
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/CategoryDefinitionsList.js b/server/sonar-web/src/main/js/apps/settings/components/CategoryDefinitionsList.js new file mode 100644 index 00000000000..cc2e6d8c24e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/CategoryDefinitionsList.js @@ -0,0 +1,37 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import { connect } from 'react-redux'; +import SubCategoryDefinitionsList from './SubCategoryDefinitionsList'; +import { getSettingsForCategory } from '../store/rootReducer'; + +class CategoryDefinitionsList extends React.Component { + render () { + return ; + } +} + +const mapStateToProps = (state, ownProps) => ({ + settings: getSettingsForCategory(state, ownProps.category) +}); + +export default connect( + mapStateToProps +)(CategoryDefinitionsList); diff --git a/server/sonar-web/src/main/js/apps/settings/components/Definition.js b/server/sonar-web/src/main/js/apps/settings/components/Definition.js new file mode 100644 index 00000000000..66b19fd023a --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/Definition.js @@ -0,0 +1,195 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import { connect } from 'react-redux'; +import shallowCompare from 'react-addons-shallow-compare'; +import classNames from 'classnames'; +import Input from './inputs/Input'; +import DefinitionDefaults from './DefinitionDefaults'; +import DefinitionChanges from './DefinitionChanges'; +import { getPropertyName, getPropertyDescription, isEmptyValue, getSettingValue, isDefaultOrInherited } from '../utils'; +import { translateWithParameters, translate } from '../../../helpers/l10n'; +import { resetValue, saveValue } from '../store/actions'; +import { isLoading, getValidationMessage, getChangedValue } from '../store/rootReducer'; +import { failValidation, passValidation } from '../store/settingsPage/validationMessages/actions'; +import { cancelChange, changeValue } from '../store/settingsPage/changedValues/actions'; + +class Definition extends React.Component { + static propTypes = { + component: React.PropTypes.object, + setting: React.PropTypes.object.isRequired, + changedValue: React.PropTypes.any, + loading: React.PropTypes.bool.isRequired, + validationMessage: React.PropTypes.string, + + changeValue: React.PropTypes.func.isRequired, + cancelChange: React.PropTypes.func.isRequired, + saveValue: React.PropTypes.func.isRequired, + resetValue: React.PropTypes.func.isRequired, + failValidation: React.PropTypes.func.isRequired, + passValidation: React.PropTypes.func.isRequired + }; + + state = { + success: false + }; + + componentDidMount () { + this.mounted = true; + } + + shouldComponentUpdate (nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } + + componentWillUnmount () { + this.mounted = false; + } + + safeSetState (changes) { + if (this.mounted) { + this.setState(changes); + } + } + + handleChange (value) { + clearTimeout(this.timeout); + return this.props.changeValue(this.props.setting.definition.key, value); + } + + handleReset () { + const componentKey = this.props.component ? this.props.component.key : null; + const { definition } = this.props.setting; + return this.props.resetValue(definition.key, componentKey).then(() => { + this.safeSetState({ success: true }); + this.timeout = setTimeout(() => this.safeSetState({ success: false }), 3000); + }).catch(() => { /* do nothing */ }); + } + + handleCancel () { + this.props.cancelChange(this.props.setting.definition.key); + this.props.passValidation(this.props.setting.definition.key); + } + + handleSave () { + this.safeSetState({ success: false }); + const { definition } = this.props.setting; + if (isEmptyValue(definition, this.props.changedValue)) { + this.props.failValidation(definition.key, translate('settings.state.value_cant_be_empty')); + return; + } + + const componentKey = this.props.component ? this.props.component.key : null; + this.props.saveValue(this.props.setting.definition.key, componentKey).then(() => { + this.safeSetState({ success: true }); + this.timeout = setTimeout(() => this.safeSetState({ success: false }), 3000); + }).catch(() => { /* do nothing */ }); + } + + render () { + const { setting, changedValue, loading } = this.props; + const { definition } = setting; + const propertyName = getPropertyName(definition); + + const hasValueChanged = changedValue != null; + + const className = classNames('settings-definition', { + 'settings-definition-changed': hasValueChanged + }); + + const effectiveValue = hasValueChanged ? changedValue : getSettingValue(setting); + + const isDefault = isDefaultOrInherited(setting) && !hasValueChanged; + + return ( +
+
+

+ {propertyName} +

+ +
+ +
+ {translateWithParameters('settings.key_x', definition.key)} +
+
+ +
+ + + {!hasValueChanged && ( + this.handleReset()}/> + )} + + {hasValueChanged && ( + + )} + +
+ {loading && ( + + + + + {translate('settings.state.saving')} + + )} + + {!loading && (this.props.validationMessage != null) && ( + + + + + {translateWithParameters('settings.state.validation_failed', this.props.validationMessage)} + + )} + + {!loading && this.state.success && ( + + + + + {translate('settings.state.saved')} + + )} +
+
+
+ ); + } +} + +const mapStateToProps = (state, ownProps) => ({ + changedValue: getChangedValue(state, ownProps.setting.definition.key), + loading: isLoading(state, ownProps.setting.definition.key), + validationMessage: getValidationMessage(state, ownProps.setting.definition.key) +}); + +export default connect( + mapStateToProps, + { changeValue, saveValue, resetValue, failValidation, passValidation, cancelChange } +)(Definition); diff --git a/server/sonar-web/src/main/js/apps/settings/components/DefinitionChanges.js b/server/sonar-web/src/main/js/apps/settings/components/DefinitionChanges.js new file mode 100644 index 00000000000..edbeafecf9e --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/DefinitionChanges.js @@ -0,0 +1,59 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import shallowCompare from 'react-addons-shallow-compare'; +import { translate } from '../../../helpers/l10n'; + +export default class DefinitionChanges extends React.Component { + static propTypes = { + onSave: React.PropTypes.func.isRequired, + onCancel: React.PropTypes.func.isRequired + }; + + shouldComponentUpdate (nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } + + handleSaveClick (e) { + e.preventDefault(); + e.target.blur(); + this.props.onSave(); + } + + handleCancelChange (e) { + e.preventDefault(); + e.target.blur(); + this.props.onCancel(); + } + + render () { + return ( +
+ + + +
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/DefinitionDefaults.js b/server/sonar-web/src/main/js/apps/settings/components/DefinitionDefaults.js new file mode 100644 index 00000000000..452b24c5ea5 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/DefinitionDefaults.js @@ -0,0 +1,60 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import { getSettingValue, isEmptyValue, getDefaultValue } from '../utils'; +import { translate } from '../../../helpers/l10n'; + +export default class DefinitionDefaults extends React.Component { + static propTypes = { + setting: React.PropTypes.object.isRequired, + isDefault: React.PropTypes.bool.isRequired, + onReset: React.PropTypes.func.isRequired + }; + + handleReset (e) { + e.preventDefault(); + e.target.blur(); + this.props.onReset(); + } + + render () { + const { setting, isDefault } = this.props; + const { definition } = setting; + + const isExplicitlySet = !isDefault && !isEmptyValue(definition, getSettingValue(setting)); + + return ( +
+ {isDefault && ( +
+ {translate('settings._default')} +
+ )} + + {isExplicitlySet && ( +
+ + {translate('default')}{': '}{getDefaultValue(setting)} +
+ )} +
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/DefinitionsList.js b/server/sonar-web/src/main/js/apps/settings/components/DefinitionsList.js new file mode 100644 index 00000000000..77d0d7fa95b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/DefinitionsList.js @@ -0,0 +1,45 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import shallowCompare from 'react-addons-shallow-compare'; +import Definition from './Definition'; + +export default class DefinitionsList extends React.Component { + static propTypes = { + component: React.PropTypes.object, + settings: React.PropTypes.array.isRequired + }; + + shouldComponentUpdate (nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } + + render () { + return ( +
    + {this.props.settings.map(setting => ( +
  • + +
  • + ))} +
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/GlobalMessagesContainer.js b/server/sonar-web/src/main/js/apps/settings/components/GlobalMessagesContainer.js new file mode 100644 index 00000000000..f64ef91664b --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/GlobalMessagesContainer.js @@ -0,0 +1,28 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import { connect } from 'react-redux'; +import GlobalMessages from '../../../components/controls/GlobalMessages'; +import { getGlobalMessages } from '../store/rootReducer'; + +const mapStateToProps = state => ({ + messages: getGlobalMessages(state) +}); + +export default connect(mapStateToProps)(GlobalMessages); diff --git a/server/sonar-web/src/main/js/apps/settings/components/PageHeader.js b/server/sonar-web/src/main/js/apps/settings/components/PageHeader.js new file mode 100644 index 00000000000..34e402122cb --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/PageHeader.js @@ -0,0 +1,44 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import { translate } from '../../../helpers/l10n'; + +export default class PageHeader extends React.Component { + static propTypes = { + component: React.PropTypes.object + }; + + render () { + const title = this.props.component != null ? + translate('project_settings.page') : + translate('settings.page'); + + const description = this.props.component != null ? + translate('project_settings.page.description') : + translate('settings.page.description'); + + return ( +
+

{title}

+
{description}
+
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.js b/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.js new file mode 100644 index 00000000000..f61d6b2aa7a --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/SubCategoryDefinitionsList.js @@ -0,0 +1,62 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import shallowCompare from 'react-addons-shallow-compare'; +import groupBy from 'lodash/groupBy'; +import sortBy from 'lodash/sortBy'; +import DefinitionsList from './DefinitionsList'; +import { getSubCategoryName, getSubCategoryDescription } from '../utils'; + +export default class SubCategoryDefinitionsList extends React.Component { + static propTypes = { + component: React.PropTypes.object, + settings: React.PropTypes.array.isRequired + }; + + shouldComponentUpdate (nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } + + render () { + const bySubCategory = groupBy(this.props.settings, setting => setting.definition.subCategory); + const subCategories = Object.keys(bySubCategory).map(key => ({ + key, + name: getSubCategoryName(bySubCategory[key][0].definition.category, key), + description: getSubCategoryDescription(bySubCategory[key][0].definition.category, key) + })); + const sortedSubCategories = sortBy(subCategories, subCategory => subCategory.name.toLowerCase()); + + return ( +
    + {sortedSubCategories.map(subCategory => ( +
  • +

    {subCategory.name}

    + {subCategory.description != null && ( +
    + {subCategory.description} +
    + )} + +
  • + ))} +
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/Input.js b/server/sonar-web/src/main/js/apps/settings/components/inputs/Input.js new file mode 100644 index 00000000000..02fcbfacef2 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/Input.js @@ -0,0 +1,51 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import shallowCompare from 'react-addons-shallow-compare'; +import PropertySetInput from './PropertySetInput'; +import MultiValueInput from './MultiValueInput'; +import PrimitiveInput from './PrimitiveInput'; +import { TYPE_PROPERTY_SET } from '../../constants'; + +export default class Input extends React.Component { + static propTypes = { + setting: React.PropTypes.object.isRequired, + value: React.PropTypes.any, + onChange: React.PropTypes.func.isRequired + }; + + shouldComponentUpdate (nextProps, nextState) { + return shallowCompare(this, nextProps, nextState); + } + + render () { + const { definition } = this.props.setting; + + if (definition.multiValues) { + return ; + } + + if (definition.type === TYPE_PROPERTY_SET) { + return ; + } + + return ; + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForBoolean.js b/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForBoolean.js new file mode 100644 index 00000000000..9f640591835 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForBoolean.js @@ -0,0 +1,48 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import Toggle from '../../../../components/controls/Toggle'; +import { defaultInputPropTypes } from '../../propTypes'; +import { translate } from '../../../../helpers/l10n'; + +export default class InputForBoolean extends React.Component { + static propTypes = { + ...defaultInputPropTypes, + value: React.PropTypes.oneOfType([React.PropTypes.bool, React.PropTypes.string]) + }; + + render () { + const hasValue = this.props.value != null; + const displayedValue = hasValue ? this.props.value : false; + + return ( +
+ + + {!hasValue && ( + {translate('settings.not_set')} + )} +
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForNumber.js b/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForNumber.js new file mode 100644 index 00000000000..6f741da3730 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForNumber.js @@ -0,0 +1,29 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import SimpleInput from './SimpleInput'; + +export default class InputForNumber extends React.Component { + render () { + return ( + + ); + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForPassword.js b/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForPassword.js new file mode 100644 index 00000000000..4e2a783ce11 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForPassword.js @@ -0,0 +1,89 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import { translate } from '../../../../helpers/l10n'; +import { defaultInputPropTypes } from '../../propTypes'; + +export default class InputForPassword extends React.Component { + static propTypes = defaultInputPropTypes; + + state = { + changing: false + }; + + handleChangeClick (e) { + e.preventDefault(); + e.target.blur(); + this.setState({ changing: true }); + } + + handleCancelChangeClick (e) { + e.preventDefault(); + e.target.blur(); + this.setState({ changing: false }); + } + + handleFormSubmit (e) { + e.preventDefault(); + this.props.onChange(this.refs.input.value); + this.setState({ changing: false }); + } + + renderInput () { + return ( +
+
this.handleFormSubmit(e)}> + + + + this.handleCancelChangeClick(e)}> + {translate('cancel')} + +
+
+ ); + } + + render () { + if (this.state.changing) { + return this.renderInput(); + } + + const hasValue = !!this.props.value; + + return ( +
+ {hasValue && ( + + )} + + +
+ ); + } +} diff --git a/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForSingleSelectList.js b/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForSingleSelectList.js new file mode 100644 index 00000000000..a0c704623d9 --- /dev/null +++ b/server/sonar-web/src/main/js/apps/settings/components/inputs/InputForSingleSelectList.js @@ -0,0 +1,50 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +import React from 'react'; +import Select from 'react-select'; +import { defaultInputPropTypes } from '../../propTypes'; + +export default class InputForSingleSelectList extends React.Component { + static propTypes = { + ...defaultInputPropTypes, + options: React.PropTypes.arrayOf(React.PropTypes.string).isRequired + }; + + handleInputChange (option) { + this.props.onChange(option.value); + } + + render () { + const options = this.props.options.map(option => ({ + label: option, + value: option + })); + + return ( + -<% - else - license = controller.java_facade.parseLicense(value) - product = license.getProduct() || '-' - # super-hack here - # should be avoided in the future - does_product_match = property.key.include? product -%> -
- - -
- - - - - - - - - - - - - - - - - - - - - - <% license.additionalProperties().each do |k,v| -%> - - - - - <% end %> -
Product:<%= product -%>
Organization:<%= license.getOrganization() || '-' -%>
Expiration: - <% if license.getExpirationDate() - formatted_date = l(Date.parse(license.getExpirationDateAsString())) - %> - <%= license.isExpired() ? "#{formatted_date}" : formatted_date -%> - <% else %> - - - <% end %> - -
Type:<%= license.getType() || '-' -%>
Server: - <% if license.getServer() && - license.getServer() != "*" && - controller.java_facade.getConfigurationValue("sonar.server_id") != license.getServer() %> - <%= license.getServer() -%> - <% else %> - <%= license.getServer() || '-' -%> - <% end %> -
<%= k -%>:<%= v || '-' -%>
-
-
-<% end %> diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_METRIC.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_METRIC.html.erb deleted file mode 100644 index 18e706b7f26..00000000000 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_METRIC.html.erb +++ /dev/null @@ -1,24 +0,0 @@ -<% - defaultValue = (defined? property.defaultValue) ? property.defaultValue : nil -%> - \ No newline at end of file diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_PASSWORD.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_PASSWORD.html.erb deleted file mode 100644 index d8c1556936c..00000000000 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_PASSWORD.html.erb +++ /dev/null @@ -1,7 +0,0 @@ -<% - options = {:id => id} - options[:size] = (defined? size) ? size : nil -%> - -<% value = Property::EXISTING_PASSWORD unless value.blank? %> -<%= property_input_field(name, PropertyType::TYPE_PASSWORD, value, PropertiesHelper::SCREEN_SETTINGS, options) %> diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_PROPERTY_SET.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_PROPERTY_SET.html.erb deleted file mode 100644 index ae8159a9df4..00000000000 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_PROPERTY_SET.html.erb +++ /dev/null @@ -1,11 +0,0 @@ -<% choices = Property.values(property.propertySetKey).reject(&:blank?) -%> -<% prompt = [[message('default'), '']] -%> - -<% if !value.blank? && (choices.exclude? value) -%> - <%= image_tag 'exclamation.png' -%> - <% missing = [[h(value + ' <' + message('deleted') + '>'), value]] -%> -<% else -%> - <% missing = [] -%> -<% end -%> - -<%= select_tag name, options_for_select(prompt + choices + missing, value), :id => id -%> \ No newline at end of file diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_PROPERTY_SET_DEFINITION.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_PROPERTY_SET_DEFINITION.html.erb deleted file mode 100644 index beec593636c..00000000000 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_PROPERTY_SET_DEFINITION.html.erb +++ /dev/null @@ -1,38 +0,0 @@ -<% resource_id = @resource.id if @resource -%> - - - - - <% unless key_field(property) -%> - <%= hidden_field_tag "auto_generate[#{property.key}]", true -%> - <% end -%> - <% property.fields.each do |field| -%> - - <% end -%> - - - - - - <% set_keys = Property.values(property.key, resource_id) -%> - <% set_keys = [''] if set_keys.all?(&:blank?) -%> - <% set_keys.each_with_index do |set_key, index| -%> - <%= render 'settings/set_instance', :property => property, :set_key => set_key, :resource_id => resource_id, :hide_delete => (index == 0) %> - <% end -%> - <%= render 'settings/set_instance', :property => property, :set_key => nil, :resource_id => resource_id, :hide_delete => false %> - - - - - - - -
- <%= field_name(property, field) -%> - <% desc = field_description(property, field) -%> - <% unless desc.blank? %> -

<%= desc -%>

- <% end -%> -
- -
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_REGULAR_EXPRESSION.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_REGULAR_EXPRESSION.html.erb deleted file mode 100644 index 8274fd8ef8f..00000000000 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_REGULAR_EXPRESSION.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -<% - options = {:id => id} - options[:size] = (defined? size) ? size : nil -%> -<%= property_input_field(name, PropertyType::TYPE_REGULAR_EXPRESSION, value, PropertiesHelper::SCREEN_SETTINGS, options) %> \ No newline at end of file diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_SINGLE_SELECT_LIST.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_SINGLE_SELECT_LIST.html.erb deleted file mode 100644 index 4153c332d13..00000000000 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_SINGLE_SELECT_LIST.html.erb +++ /dev/null @@ -1,2 +0,0 @@ -<%= property_input_field(name, PropertyType::TYPE_SINGLE_SELECT_LIST, value, PropertiesHelper::SCREEN_SETTINGS, - {:id => id, :default => (defined? property.defaultValue) ? property.defaultValue : nil, :values => property.options, :extra_values => {:property => property, :field => field}}) %> \ No newline at end of file diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_STRING.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_STRING.html.erb deleted file mode 100644 index b28c75604a7..00000000000 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_STRING.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -<% - options = {:id => id} - options[:size] = (defined? size) ? size : nil -%> -<%= property_input_field(name, PropertyType::TYPE_STRING, value, PropertiesHelper::SCREEN_SETTINGS, options) %> \ No newline at end of file diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_TEXT.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_TEXT.html.erb deleted file mode 100644 index af46cf40082..00000000000 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_TEXT.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -<% - options = {:id => id} - options[:size] = (defined? size) ? size : nil -%> -<%= property_input_field(name, PropertyType::TYPE_TEXT, value, PropertiesHelper::SCREEN_SETTINGS, options) %> diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_USER_LOGIN.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_USER_LOGIN.html.erb deleted file mode 100644 index 6f94fa6a651..00000000000 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/_type_USER_LOGIN.html.erb +++ /dev/null @@ -1,5 +0,0 @@ -<% - options = {:id => id} - options[:size] = (defined? size) ? size : nil -%> -<%= property_input_field(name, PropertyType::TYPE_USER_LOGIN, value, PropertiesHelper::SCREEN_SETTINGS, options) %> diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/index.html.erb index 55ae2ad65ec..6c42ad1d4b7 100644 --- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/index.html.erb +++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/settings/index.html.erb @@ -1,3 +1,3 @@ -
- <%= render 'settings', :project => nil %> -
+<% content_for :extra_script do %> + +<% end %> diff --git a/server/sonar-web/tests/utils.js b/server/sonar-web/tests/utils.js new file mode 100644 index 00000000000..afa4f53d0d4 --- /dev/null +++ b/server/sonar-web/tests/utils.js @@ -0,0 +1,38 @@ +/* + * SonarQube + * Copyright (C) 2009-2016 SonarSource SA + * mailto:contact AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +export const click = element => { + return element.simulate('click', { + target: { blur () {} }, + preventDefault () {} + }); +}; + +export const submit = element => { + return element.simulate('submit', { + preventDefault () {} + }); +}; + +export const change = (element, value) => { + return element.simulate('change', { + target: { value }, + currentTarget: { value } + }); +}; diff --git a/sonar-application/pom.xml b/sonar-application/pom.xml index 8656a2f2718..3397de70d90 100644 --- a/sonar-application/pom.xml +++ b/sonar-application/pom.xml @@ -238,8 +238,8 @@ - 120000000 - 128000000 + 125000000 + 132000000 ${project.build.directory}/sonarqube-${project.version}.zip diff --git a/sonar-core/src/main/resources/org/sonar/l10n/core.properties b/sonar-core/src/main/resources/org/sonar/l10n/core.properties index a21b3887b78..8b42dba8ba5 100644 --- a/sonar-core/src/main/resources/org/sonar/l10n/core.properties +++ b/sonar-core/src/main/resources/org/sonar/l10n/core.properties @@ -147,6 +147,7 @@ search_verb=Search see_all=See All select_all=Select all select_verb=Select +set=Set severity=Severity severity_abbreviated=Se. shared=Shared @@ -967,6 +968,20 @@ dashboard.default_dashboard=This dashboard is the default one and is displayed w #------------------------------------------------------------------------------ settings.add=Add value settings.save_category=Save {0} Settings +settings.key_x=Key: {0} +settings.default_x=Default: {0} +settings.not_set=(not set) +settings.state.saving=Saving... +settings.state.saved=Saved! +settings.state.validation_failed=Validation failed. {0} +settings.state.value_cant_be_empty=Value can't be empty. Use "Reset" to set value to the default one. +settings._default=(default) +settings.boolean.true=True +settings.boolean.false=False +settings.default.no_value= +settings.default.complex_value= +settings.default.password= + property.category.general=General property.category.general.email=Email property.category.general.duplications=Duplications