diff options
author | Stas Vilchik <vilchiks@gmail.com> | 2017-03-27 14:00:14 +0200 |
---|---|---|
committer | Stas Vilchik <stas-vilchik@users.noreply.github.com> | 2017-04-03 10:38:52 +0200 |
commit | 32a73efa05cb12056a93f08b9124e647213f1f02 (patch) | |
tree | 89c545631a613d2041383b5143afb0e040c327e0 | |
parent | be8738ea1e0322d81e238c4462c7ec6f22d2177c (diff) | |
download | sonarqube-32a73efa05cb12056a93f08b9124e647213f1f02.tar.gz sonarqube-32a73efa05cb12056a93f08b9124e647213f1f02.zip |
SONAR-9008 support quality profiles for organizations
76 files changed, 2383 insertions, 513 deletions
diff --git a/it/it-tests/src/test/java/it/Category5Suite.java b/it/it-tests/src/test/java/it/Category5Suite.java index 9050e43e2a2..5bd6f18cc49 100644 --- a/it/it-tests/src/test/java/it/Category5Suite.java +++ b/it/it-tests/src/test/java/it/Category5Suite.java @@ -20,6 +20,7 @@ package it; import it.organization.OrganizationMembershipTest; +import it.organization.OrganizationQualityProfilesPageTest; import it.serverSystem.ClusterTest; import it.serverSystem.RestartTest; import it.serverSystem.ServerSystemRestartingOrchestrator; @@ -47,7 +48,8 @@ import org.junit.runners.Suite; UpdateCenterTest.class, RealmAuthenticationTest.class, SsoAuthenticationTest.class, - OrganizationMembershipTest.class + OrganizationMembershipTest.class, + OrganizationQualityProfilesPageTest.class }) public class Category5Suite { diff --git a/it/it-tests/src/test/java/it/organization/OrganizationQualityProfilesPageTest.java b/it/it-tests/src/test/java/it/organization/OrganizationQualityProfilesPageTest.java new file mode 100644 index 00000000000..3cabbe545d0 --- /dev/null +++ b/it/it-tests/src/test/java/it/organization/OrganizationQualityProfilesPageTest.java @@ -0,0 +1,206 @@ +/* + * SonarQube + * Copyright (C) 2009-2017 SonarSource SA + * mailto:info AT sonarsource DOT com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package it.organization; + +import com.codeborne.selenide.Condition; +import com.sonar.orchestrator.Orchestrator; +import com.sonar.orchestrator.build.SonarScanner; +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.ClassRule; +import org.junit.Ignore; +import org.junit.Test; +import org.sonarqube.ws.client.PostRequest; +import org.sonarqube.ws.client.WsClient; +import org.sonarqube.ws.client.organization.CreateWsRequest; +import pageobjects.Navigation; + +import static com.codeborne.selenide.Selenide.$; +import static java.util.Collections.emptyMap; +import static util.ItUtils.newAdminWsClient; +import static util.ItUtils.projectDir; +import static util.ItUtils.xooPlugin; +import static util.selenium.Selenese.runSelenese; + +public class OrganizationQualityProfilesPageTest { + + @ClassRule + public static final Orchestrator orchestrator = Orchestrator.builderEnv() + .addPlugin(xooPlugin()) + .build(); + + private static WsClient adminWsClient; + private static final String ORGANIZATION = "test-org"; + + @BeforeClass + public static void setUp() { + adminWsClient = newAdminWsClient(orchestrator); + orchestrator.resetData(); + orchestrator.getServer().post("api/organizations/enable_support", emptyMap()); + createOrganization(); + } + + @Before + public void createSampleProfile() { + createProfile("xoo", "sample"); + inheritProfile("xoo", "sample", "Basic"); + analyzeProject("shared/xoo-sample"); + addProfileToProject("xoo", "sample", "sample"); + } + + @After + public void deleteSampleProfile() { + setDefault("xoo", "Basic"); + deleteProfile("xoo", "sample"); + deleteProfile("xoo", "new name"); + } + + @Test + public void testNoGlobalPage(){ + Navigation nav = Navigation.get(orchestrator); + nav.open("/profiles"); + $(".page-wrapper-simple").should(Condition.visible); + } + + @Test + public void testHomePage() throws Exception { + runSelenese(orchestrator, + "/organization/OrganizationQualityProfilesPageTest/should_display_list.html", + "/organization/OrganizationQualityProfilesPageTest/should_open_from_list.html", + "/organization/OrganizationQualityProfilesPageTest/should_filter_by_language.html"); + } + + @Test + public void testProfilePage() throws Exception { + runSelenese(orchestrator, + "/organization/OrganizationQualityProfilesPageTest/should_display_profile_rules.html", + "/organization/OrganizationQualityProfilesPageTest/should_display_profile_inheritance.html", + "/organization/OrganizationQualityProfilesPageTest/should_display_profile_projects.html", + "/organization/OrganizationQualityProfilesPageTest/should_display_profile_exporters.html"); + } + + @Test + public void testNotFound() { + Navigation nav = Navigation.get(orchestrator); + nav.open("/organizations/test-org/quality_profiles/show?key=unknown"); + $(".quality-profile-not-found").should(Condition.visible); + } + + @Test + public void testProfileChangelog() throws Exception { + runSelenese(orchestrator, + "/organization/OrganizationQualityProfilesPageTest/should_display_changelog.html"); + } + + @Ignore("find a way to know profile key inside selenium tests") + @Test + public void testComparison() throws Exception { + runSelenese(orchestrator, "/organization/OrganizationQualityProfilesPageTest/should_compare.html"); + } + + @Test + public void testCreation() throws Exception { + runSelenese(orchestrator, "/organization/OrganizationQualityProfilesPageTest/should_create.html"); + } + + @Test + public void testDeletion() throws Exception { + runSelenese(orchestrator, "/organization/OrganizationQualityProfilesPageTest/should_delete.html"); + } + + @Test + public void testCopying() throws Exception { + runSelenese(orchestrator, "/organization/OrganizationQualityProfilesPageTest/should_copy.html"); + } + + @Test + public void testRenaming() throws Exception { + runSelenese(orchestrator, "/organization/OrganizationQualityProfilesPageTest/should_rename.html"); + } + + @Test + public void testSettingDefault() throws Exception { + runSelenese(orchestrator, "/organization/OrganizationQualityProfilesPageTest/should_set_default.html"); + } + + @Test + public void testRestoration() throws Exception { + deleteProfile("xoo", "empty"); + + runSelenese(orchestrator, + "/organization/OrganizationQualityProfilesPageTest/should_restore.html", + "/organization/OrganizationQualityProfilesPageTest/should_restore_built_in.html"); + } + + private static void createProfile(String language, String name) { + adminWsClient.wsConnector().call( + new PostRequest("api/qualityprofiles/create") + .setParam("language", language) + .setParam("name", name) + .setParam("organization", ORGANIZATION)); + } + + private static void inheritProfile(String language, String name, String parentName) { + adminWsClient.wsConnector().call( + new PostRequest("api/qualityprofiles/change_parent") + .setParam("language", language) + .setParam("profileName", name) + .setParam("parentName", parentName) + .setParam("organization", ORGANIZATION)); + } + + private static void analyzeProject(String path) { + orchestrator.executeBuild(SonarScanner.create(projectDir(path)).setProperties( + "sonar.organization", ORGANIZATION, + "sonar.login", "admin", + "sonar.password", "admin" + )); + } + + private static void addProfileToProject(String language, String profileName, String projectKey) { + adminWsClient.wsConnector().call( + new PostRequest("api/qualityprofiles/add_project") + .setParam("language", language) + .setParam("profileName", profileName) + .setParam("organization", ORGANIZATION) + .setParam("projectKey", projectKey)); + } + + private static void deleteProfile(String language, String name) { + adminWsClient.wsConnector().call( + new PostRequest("api/qualityprofiles/delete") + .setParam("language", language) + .setParam("profileName", name) + .setParam("organization", ORGANIZATION)); + } + + private static void setDefault(String language, String name) { + adminWsClient.wsConnector().call( + new PostRequest("api/qualityprofiles/set_default") + .setParam("language", language) + .setParam("profileName", name) + .setParam("organization", ORGANIZATION)); + } + + private static void createOrganization() { + adminWsClient.organizations().create(new CreateWsRequest.Builder().setKey(ORGANIZATION).setName(ORGANIZATION).build()); + } +} diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_compare.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_compare.html new file mode 100644 index 00000000000..b8ea98d1690 --- /dev/null +++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_compare.html @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> + <link rel="selenium.base" href="http://localhost:49506"/> + <title>should_display_changelog</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr> +<td rowspan="1" colspan="3">should_display_changelog</td> +</tr> +</thead> +<tbody> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profiles-table-name a[href^="/organizations/test-org/quality_profiles/show?key=xoo-sample"]</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=.quality-profiles-table-name a[href^="/organizations/test-org/quality_profiles/show?key=xoo-sample"]</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profile-header .dropdown-toggle</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=.quality-profile-header .dropdown-toggle</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=#quality-profile-compare</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.js-profile-comparison .Select</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=.js-profile-comparison .Select-control</td> + <td></td> +</tr> +</tbody> +</table> +</body> +</html> diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_copy.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_copy.html new file mode 100644 index 00000000000..e233ad8f005 --- /dev/null +++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_copy.html @@ -0,0 +1,110 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> + <link rel="selenium.base" href="http://localhost:49506"/> + <title>should_create</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr> +<td rowspan="1" colspan="3">should_create</td> +</tr> +</thead> +<tbody> +<tr> + <td>open</td> + <td>/sessions/logout</td> + <td></td> +</tr> +<tr> + <td>open</td> + <td>/sessions/login</td> + <td></td> +</tr> +<tr> + <td>type</td> + <td>id=password</td> + <td>admin</td> +</tr> +<tr> + <td>type</td> + <td>id=login</td> + <td>admin</td> +</tr> +<tr> + <td>clickAndWait</td> + <td>name=commit</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.js-user-authenticated</td> + <td></td> +</tr> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=table[data-language="xoo"] tr[data-name="sample"] .quality-profiles-table-name a</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=table[data-language="xoo"] tr[data-name="sample"] .quality-profiles-table-name a</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profile-header .dropdown-toggle</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=.quality-profile-header .dropdown-toggle</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=#quality-profile-copy</td> + <td></td> +</tr> +<tr> + <td>type</td> + <td>css=#copy-profile-name</td> + <td>copied</td> +</tr> +<tr> + <td>click</td> + <td>css=#copy-profile-submit</td> + <td></td> +</tr> +<tr> + <td>waitForText</td> + <td>css=.quality-profile-header</td> + <td>*copied*</td> +</tr> +<tr> + <td>waitForText</td> + <td>css=.quality-profile-rules</td> + <td>*1*Bugs*0*Vulnerabilities*0*Code Smells*1*</td> +</tr> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=table[data-language="xoo"] tr[data-name="copied"]</td> + <td></td> +</tr> +</tbody> +</table> +</body> +</html> diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_create.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_create.html new file mode 100644 index 00000000000..253880e05b4 --- /dev/null +++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_create.html @@ -0,0 +1,110 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> + <link rel="selenium.base" href="http://localhost:49506"/> + <title>should_create</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr> +<td rowspan="1" colspan="3">should_create</td> +</tr> +</thead> +<tbody> +<tr> + <td>open</td> + <td>/sessions/logout</td> + <td></td> +</tr> +<tr> + <td>open</td> + <td>/sessions/login</td> + <td></td> +</tr> +<tr> + <td>type</td> + <td>id=password</td> + <td>admin</td> +</tr> +<tr> + <td>type</td> + <td>id=login</td> + <td>admin</td> +</tr> +<tr> + <td>clickAndWait</td> + <td>name=commit</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.js-user-authenticated</td> + <td></td> +</tr> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=#quality-profiles-create</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=#quality-profiles-create</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=#create-profile-name</td> + <td></td> +</tr> +<tr> + <td>assertElementPresent</td> + <td>css=#create-profile-form-backup-XooProfileImporter</td> + <td></td> +</tr> +<tr> + <td>type</td> + <td>css=#create-profile-name</td> + <td>test</td> +</tr> +<tr> + <td>click</td> + <td>css=#create-profile-submit</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profile-header</td> + <td></td> +</tr> +<tr> + <td>waitForText</td> + <td>css=.quality-profile-header</td> + <td>*test*</td> +</tr> +<tr> + <td>waitForText</td> + <td>css=.quality-profile-rules</td> + <td>*Bugs*0*Vulnerabilities*0*Code Smells*0*</td> +</tr> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profiles-table-row[data-name="test"]</td> + <td></td> +</tr> +</tbody> +</table> +</body> +</html> diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_delete.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_delete.html new file mode 100644 index 00000000000..bdaab3efc06 --- /dev/null +++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_delete.html @@ -0,0 +1,115 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> + <link rel="selenium.base" href="http://localhost:49506"/> + <title>should_create</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr> +<td rowspan="1" colspan="3">should_create</td> +</tr> +</thead> +<tbody> +<tr> + <td>open</td> + <td>/sessions/logout</td> + <td></td> +</tr> +<tr> + <td>open</td> + <td>/sessions/login</td> + <td></td> +</tr> +<tr> + <td>type</td> + <td>id=password</td> + <td>admin</td> +</tr> +<tr> + <td>type</td> + <td>id=login</td> + <td>admin</td> +</tr> +<tr> + <td>clickAndWait</td> + <td>name=commit</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.js-user-authenticated</td> + <td></td> +</tr> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=table[data-language="xoo"] tr[data-name="sample"] .quality-profiles-table-name a</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=table[data-language="xoo"] tr[data-name="sample"] .quality-profiles-table-name a</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profile-header .dropdown-toggle</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=.quality-profile-header .dropdown-toggle</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=#quality-profile-delete</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=#delete-profile-submit</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=#delete-profile-submit</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=table[data-language="xoo"] tr[data-name="Basic"]</td> + <td></td> +</tr> +<tr> + <td>waitForElementNotPresent</td> + <td>css=table[data-language="xoo"] tr[data-name="sample"]</td> + <td></td> +</tr> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=table[data-language="xoo"] tr[data-name="Basic"]</td> + <td></td> +</tr> +<tr> + <td>assertElementNotPresent</td> + <td>css=table[data-language="xoo"] tr[data-name="sample"]</td> + <td></td> +</tr> +</tbody> +</table> +</body> +</html> diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_changelog.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_changelog.html new file mode 100644 index 00000000000..417700e47db --- /dev/null +++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_changelog.html @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> + <link rel="selenium.base" href="http://localhost:49506"/> + <title>should_display_changelog</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr> +<td rowspan="1" colspan="3">should_display_changelog</td> +</tr> +</thead> +<tbody> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=table[data-language="xoo"] tr[data-name="Basic"] .quality-profiles-table-name a</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=table[data-language="xoo"] tr[data-name="Basic"] .quality-profiles-table-name a</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=a[href^="/organizations/test-org/quality_profiles/changelog"]</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=a[href^="/organizations/test-org/quality_profiles/changelog"]</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.js-profile-changelog-event</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>css=.js-profile-changelog-event</td> + <td>*Administrator*Activated*Has Tag*Major*tag*xoo*</td> +</tr> +</tbody> +</table> +</body> +</html> diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_list.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_list.html new file mode 100644 index 00000000000..b3341945d1c --- /dev/null +++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_list.html @@ -0,0 +1,74 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> + <title>should_display_list</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr> +<td rowspan="1" colspan="3">should_display_list</td> +</tr> +</thead> +<tbody> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profiles-table</td> + <td></td> +</tr> +<tr> + <td>assertElementPresent</td> + <td>css=.quality-profiles-table .data[data-language="xoo"]</td> + <td></td> +</tr> +<tr> + <td>assertElementPresent</td> + <td>css=.quality-profiles-table .data[data-language="xoo2"]</td> + <td></td> +</tr> +<tr> + <td>assertElementPresent</td> + <td>css=table[data-language="xoo"] tr[data-name="Basic"]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>css=table[data-language="xoo"] tr[data-name="Basic"] .quality-profiles-table-name</td> + <td>*Basic*</td> +</tr> +<tr> + <td>assertText</td> + <td>css=table[data-language="xoo"] tr[data-name="Basic"] .quality-profiles-table-projects</td> + <td>*Default*</td> +</tr> +<tr> + <td>assertText</td> + <td>css=table[data-language="xoo"] tr[data-name="Basic"] .quality-profiles-table-rules</td> + <td>*1*</td> +</tr> +<tr> + <td>assertElementPresent</td> + <td>css=table[data-language="xoo"] tr[data-name="Basic"] .quality-profiles-table-rules a[href^="/organizations/test-org/rules#qprofile"]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>css=table[data-language="xoo"] tr[data-name="empty"] .quality-profiles-table-projects</td> + <td>*0*</td> +</tr> +<tr> + <td>assertText</td> + <td>css=table[data-language="xoo2"] tr[data-name="Basic"] .quality-profiles-table-name</td> + <td>*Basic*</td> +</tr> +</tbody> +</table> +</body> +</html> diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_exporters.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_exporters.html new file mode 100644 index 00000000000..b26d162f2e7 --- /dev/null +++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_exporters.html @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> + <link rel="selenium.base" href="http://localhost:49506"/> + <title>should_display_profile_projects.html</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr> +<td rowspan="1" colspan="3">should_display_profile_projects.html</td> +</tr> +</thead> +<tbody> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=table[data-language="xoo"] tr[data-name="sample"] .quality-profiles-table-name a</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=table[data-language="xoo"] tr[data-name="sample"] .quality-profiles-table-name a</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profile-exporters</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profile-exporters [data-key="XooFakeExporter"]</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profile-exporters a[href^="/api/qualityprofiles/export"]</td> + <td></td> +</tr> +</tbody> +</table> +</body> +</html> diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_inheritance.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_inheritance.html new file mode 100644 index 00000000000..c403fbf5196 --- /dev/null +++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_inheritance.html @@ -0,0 +1,60 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> + <link rel="selenium.base" href="http://localhost:49506"/> + <title>should_display_profile_inheritance.html</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr> +<td rowspan="1" colspan="3">should_display_profile_inheritance.html</td> +</tr> +</thead> +<tbody> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=table[data-language="xoo"] tr[data-name="sample"] .quality-profiles-table-name a</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=table[data-language="xoo"] tr[data-name="sample"] .quality-profiles-table-name a</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profile-inheritance</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.js-inheritance-ancestor</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>css=.js-inheritance-ancestor</td> + <td>*Basic*1*</td> +</tr> +<tr> + <td>assertElementPresent</td> + <td>css=.js-inheritance-current</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>css=.js-inheritance-current</td> + <td>*sample*1*</td> +</tr> +</tbody> +</table> +</body> +</html> diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_projects.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_projects.html new file mode 100644 index 00000000000..062014eb239 --- /dev/null +++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_projects.html @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> + <link rel="selenium.base" href="http://localhost:49506"/> + <title>should_display_profile_projects.html</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr> +<td rowspan="1" colspan="3">should_display_profile_projects.html</td> +</tr> +</thead> +<tbody> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=table[data-language="xoo"] tr[data-name="sample"] .quality-profiles-table-name a</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=table[data-language="xoo"] tr[data-name="sample"] .quality-profiles-table-name a</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profile-projects</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.js-profile-project</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>css=.js-profile-project</td> + <td>*Sample*</td> +</tr> +</tbody> +</table> +</body> +</html> diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_rules.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_rules.html new file mode 100644 index 00000000000..6b497fc7b58 --- /dev/null +++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_display_profile_rules.html @@ -0,0 +1,45 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> + <link rel="selenium.base" href="http://localhost:49506"/> + <title>should_display_profile_rules.html</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr> +<td rowspan="1" colspan="3">should_display_profile_rules.html</td> +</tr> +</thead> +<tbody> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=table[data-language="xoo"] tr[data-name="Basic"] .quality-profiles-table-name a</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=table[data-language="xoo"] tr[data-name="Basic"] .quality-profiles-table-name a</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profile-rules</td> + <td></td> +</tr> +<tr> + <td>waitForText</td> + <td>css=.quality-profile-rules</td> + <td>*Active*1*Bugs*0*Vulnerabilities*0*Code Smells*1*</td> +</tr> +</tbody> +</table> +</body> +</html> diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_filter_by_language.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_filter_by_language.html new file mode 100644 index 00000000000..a913a70a34f --- /dev/null +++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_filter_by_language.html @@ -0,0 +1,89 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> + <title>should_filter_by_language</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr> +<td rowspan="1" colspan="3">should_filter_by_language</td> +</tr> +</thead> +<tbody> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profiles-table .data[data-language="xoo"]</td> + <td></td> +</tr> +<tr> + <td>assertElementPresent</td> + <td>css=.quality-profiles-table .data[data-language="xoo2"]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>css=.js-language-filter</td> + <td>*All*</td> +</tr> +<tr> + <td>click</td> + <td>css=.js-language-filter</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.js-language-filter-option[data-language="xoo2"]</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=.js-language-filter-option[data-language="xoo2"]</td> + <td></td> +</tr> +<tr> + <td>waitForElementNotPresent</td> + <td>css=.quality-profiles-table[data-language="xoo"]</td> + <td></td> +</tr> +<tr> + <td>assertElementPresent</td> + <td>css=.quality-profiles-table .data[data-language="xoo2"]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>css=.js-language-filter</td> + <td>*Xoo2*</td> +</tr> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles?language=xoo2</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profiles-table .data[data-language="xoo2"]</td> + <td></td> +</tr> +<tr> + <td>assertElementNotPresent</td> + <td>css=.quality-profiles-table[data-language="xoo"]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>css=.js-language-filter</td> + <td>*Xoo2*</td> +</tr> +</tbody> +</table> +</body> +</html> diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_open_from_list.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_open_from_list.html new file mode 100644 index 00000000000..2e753ba1cd7 --- /dev/null +++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_open_from_list.html @@ -0,0 +1,54 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> + <title>should_open_from_list</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr> +<td rowspan="1" colspan="3">should_open_from_list</td> +</tr> +</thead> +<tbody> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=table[data-language="xoo"] tr[data-name="Basic"]</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=table[data-language="xoo"] tr[data-name="Basic"] .quality-profiles-table-name a</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profile-header</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profile-rules</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profile-inheritance</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profile-projects</td> + <td></td> +</tr> +</tbody> +</table> +</body> +</html> diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_rename.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_rename.html new file mode 100644 index 00000000000..c0c437e809f --- /dev/null +++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_rename.html @@ -0,0 +1,110 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> + <link rel="selenium.base" href="http://localhost:49506"/> + <title>should_create</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr> +<td rowspan="1" colspan="3">should_create</td> +</tr> +</thead> +<tbody> +<tr> + <td>open</td> + <td>/sessions/logout</td> + <td></td> +</tr> +<tr> + <td>open</td> + <td>/sessions/login</td> + <td></td> +</tr> +<tr> + <td>type</td> + <td>id=password</td> + <td>admin</td> +</tr> +<tr> + <td>type</td> + <td>id=login</td> + <td>admin</td> +</tr> +<tr> + <td>clickAndWait</td> + <td>name=commit</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.js-user-authenticated</td> + <td></td> +</tr> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=table[data-language="xoo"] tr[data-name="sample"] .quality-profiles-table-name a</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=table[data-language="xoo"] tr[data-name="sample"] .quality-profiles-table-name a</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profile-header .dropdown-toggle</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=.quality-profile-header .dropdown-toggle</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=#quality-profile-rename</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=#rename-profile-name</td> + <td></td> +</tr> +<tr> + <td>type</td> + <td>css=#rename-profile-name</td> + <td>new name</td> +</tr> +<tr> + <td>click</td> + <td>css=#rename-profile-submit</td> + <td></td> +</tr> +<tr> + <td>waitForText</td> + <td>css=.quality-profile-header</td> + <td>*new name*</td> +</tr> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=table[data-language="xoo"] tr[data-name="new name"] .quality-profiles-table-name a</td> + <td></td> +</tr> +</tbody> +</table> +</body> +</html> diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_restore.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_restore.html new file mode 100644 index 00000000000..1debd02fcbb --- /dev/null +++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_restore.html @@ -0,0 +1,80 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> + <link rel="selenium.base" href="http://localhost:49506"/> + <title>should_create</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr> +<td rowspan="1" colspan="3">should_create</td> +</tr> +</thead> +<tbody> +<tr> + <td>open</td> + <td>/sessions/logout</td> + <td></td> +</tr> +<tr> + <td>open</td> + <td>/sessions/login</td> + <td></td> +</tr> +<tr> + <td>type</td> + <td>id=password</td> + <td>admin</td> +</tr> +<tr> + <td>type</td> + <td>id=login</td> + <td>admin</td> +</tr> +<tr> + <td>clickAndWait</td> + <td>name=commit</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.js-user-authenticated</td> + <td></td> +</tr> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.js-more-admin-actions</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=.js-more-admin-actions</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=#quality-profiles-restore</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=#restore-profile-backup</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=#restore-profile-submit</td> + <td></td> +</tr> +</tbody> +</table> +</body> +</html> diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_restore_built_in.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_restore_built_in.html new file mode 100644 index 00000000000..82005c7102e --- /dev/null +++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_restore_built_in.html @@ -0,0 +1,95 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> + <link rel="selenium.base" href="http://localhost:49506"/> + <title>should_create</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr> +<td rowspan="1" colspan="3">should_create</td> +</tr> +</thead> +<tbody> +<tr> + <td>open</td> + <td>/sessions/logout</td> + <td></td> +</tr> +<tr> + <td>open</td> + <td>/sessions/login</td> + <td></td> +</tr> +<tr> + <td>type</td> + <td>id=password</td> + <td>admin</td> +</tr> +<tr> + <td>type</td> + <td>id=login</td> + <td>admin</td> +</tr> +<tr> + <td>clickAndWait</td> + <td>name=commit</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.js-user-authenticated</td> + <td></td> +</tr> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profiles-table-row[data-name="sample"]</td> + <td></td> +</tr> +<tr> + <td>assertElementNotPresent</td> + <td>css=.quality-profiles-table-row[data-name="empty"]</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=.js-more-admin-actions</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=#quality-profiles-restore-built-in</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=#restore-built-in-profiles-submit</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=#restore-built-in-profiles-form .alert-success</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=.js-modal-close</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profiles-table-row[data-name="empty"]</td> + <td></td> +</tr> +</tbody> +</table> +</body> +</html> diff --git a/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_set_default.html b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_set_default.html new file mode 100644 index 00000000000..bb996462b3e --- /dev/null +++ b/it/it-tests/src/test/resources/organization/OrganizationQualityProfilesPageTest/should_set_default.html @@ -0,0 +1,100 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/> + <link rel="selenium.base" href="http://localhost:49506"/> + <title>should_create</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr> +<td rowspan="1" colspan="3">should_create</td> +</tr> +</thead> +<tbody> +<tr> + <td>open</td> + <td>/sessions/logout</td> + <td></td> +</tr> +<tr> + <td>open</td> + <td>/sessions/login</td> + <td></td> +</tr> +<tr> + <td>type</td> + <td>id=password</td> + <td>admin</td> +</tr> +<tr> + <td>type</td> + <td>id=login</td> + <td>admin</td> +</tr> +<tr> + <td>clickAndWait</td> + <td>name=commit</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.js-user-authenticated</td> + <td></td> +</tr> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=table[data-language="xoo"] tr[data-name="sample"] .quality-profiles-table-name a</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=table[data-language="xoo"] tr[data-name="sample"] .quality-profiles-table-name a</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profile-header .dropdown-toggle</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=.quality-profile-header .dropdown-toggle</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>css=#quality-profile-set-as-default</td> + <td></td> +</tr> +<tr> + <td>waitForText</td> + <td>css=.quality-profile-projects</td> + <td>*Default*</td> +</tr> +<tr> + <td>open</td> + <td>/organizations/test-org/quality_profiles</td> + <td></td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>css=.quality-profiles-table-row[data-name="sample"]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>css=.quality-profiles-table-row[data-name="sample"] .quality-profiles-table-projects</td> + <td>*Default*</td> +</tr> +</tbody> +</table> +</body> +</html> diff --git a/server/sonar-web/src/main/js/api/quality-profiles.js b/server/sonar-web/src/main/js/api/quality-profiles.js index 6f482bd3a9a..2f178014559 100644 --- a/server/sonar-web/src/main/js/api/quality-profiles.js +++ b/server/sonar-web/src/main/js/api/quality-profiles.js @@ -17,14 +17,15 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import { request, checkStatus, parseJSON, getJSON, post, postJSON } from '../helpers/request'; -export function getQualityProfiles(data) { +export function getQualityProfiles(data: { organization?: string, projectKey?: string }) { const url = '/api/qualityprofiles/search'; return getJSON(url, data).then(r => r.profiles); } -export function createQualityProfile(data) { +export function createQualityProfile(data: Object) { return request('/api/qualityprofiles/create') .setMethod('post') .setData(data) @@ -33,7 +34,7 @@ export function createQualityProfile(data) { .then(parseJSON); } -export function restoreQualityProfile(data) { +export function restoreQualityProfile(data: Object) { return request('/api/qualityprofiles/restore') .setMethod('post') .setData(data) @@ -42,128 +43,80 @@ export function restoreQualityProfile(data) { .then(parseJSON); } -export function getProfileProjects(data) { +export function getProfileProjects(data: Object) { const url = '/api/qualityprofiles/projects'; return getJSON(url, data); } -export function getProfileInheritance(profileKey) { +export function getProfileInheritance(profileKey: string) { const url = '/api/qualityprofiles/inheritance'; const data = { profileKey }; return getJSON(url, data); } -export function setDefaultProfile(profileKey) { +export function setDefaultProfile(profileKey: string) { const url = '/api/qualityprofiles/set_default'; const data = { profileKey }; return post(url, data); } -/** - * Rename profile - * @param {string} key - * @param {string} name - * @returns {Promise} - */ -export function renameProfile(key, name) { +export function renameProfile(key: string, name: string) { const url = '/api/qualityprofiles/rename'; const data = { key, name }; return post(url, data); } -/** - * Copy profile - * @param {string} fromKey - * @param {string} toName - * @returns {Promise} - */ -export function copyProfile(fromKey, toName) { +export function copyProfile(fromKey: string, toName: string) { const url = '/api/qualityprofiles/copy'; const data = { fromKey, toName }; return postJSON(url, data); } -/** - * Delete profile - * @param {string} profileKey - * @returns {Promise} - */ -export function deleteProfile(profileKey) { +export function deleteProfile(profileKey: string) { const url = '/api/qualityprofiles/delete'; const data = { profileKey }; return post(url, data); } -/** - * Change profile parent - * @param {string} profileKey - * @param {string} parentKey - * @returns {Promise} - */ -export function changeProfileParent(profileKey, parentKey) { +export function changeProfileParent(profileKey: string, parentKey: string) { const url = '/api/qualityprofiles/change_parent'; const data = { profileKey, parentKey }; return post(url, data); } -/** - * Get list of available importers - * @returns {Promise} - */ export function getImporters() { const url = '/api/qualityprofiles/importers'; return getJSON(url).then(r => r.importers); } -/** - * Get list of available exporters - * @returns {Promise} - */ export function getExporters() { const url = '/api/qualityprofiles/exporters'; return getJSON(url).then(r => r.exporters); } -/** - * Restore built-in profiles - * @param {string} languageKey - * @returns {Promise} - */ -export function restoreBuiltInProfiles(languageKey) { +export function restoreBuiltInProfiles(data: Object) { const url = '/api/qualityprofiles/restore_built_in'; - const data = { language: languageKey }; return post(url, data); } -/** - * Get changelog of a quality profile - * @param {Object} data API parameters - * @returns {Promise} - */ -export function getProfileChangelog(data) { +export function getProfileChangelog(data: Object) { const url = '/api/qualityprofiles/changelog'; return getJSON(url, data); } -/** - * Compare two profiles - * @param {string} leftKey - * @param {string} rightKey - * @returns {Promise} - */ -export function compareProfiles(leftKey, rightKey) { +export function compareProfiles(leftKey: string, rightKey: string) { const url = '/api/qualityprofiles/compare'; const data = { leftKey, rightKey }; return getJSON(url, data); } -export function associateProject(profileKey, projectKey) { +export function associateProject(profileKey: string, projectKey: string) { const url = '/api/qualityprofiles/add_project'; const data = { profileKey, projectKey }; return post(url, data); } -export function dissociateProject(profileKey, projectKey) { +export function dissociateProject(profileKey: string, projectKey: string) { const url = '/api/qualityprofiles/remove_project'; const data = { profileKey, projectKey }; return post(url, data); diff --git a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js index 7eebc3d0393..90249a0e7e2 100644 --- a/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js +++ b/server/sonar-web/src/main/js/app/components/nav/global/GlobalNavMenu.js @@ -143,6 +143,7 @@ export default class GlobalNavMenu extends React.Component { render() { const governanceInstalled = this.props.appState.qualifiers.includes('VW'); + const { organizationsEnabled } = this.props.appState; return ( <ul className="nav navbar-nav"> @@ -150,7 +151,7 @@ export default class GlobalNavMenu extends React.Component { {governanceInstalled && this.renderPortfolios()} {this.renderIssuesLink()} {this.renderRulesLink()} - {this.renderProfilesLink()} + {!organizationsEnabled && this.renderProfilesLink()} {this.renderQualityGatesLink()} {this.renderAdministrationLink()} {this.renderMore()} diff --git a/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js b/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js index a38d9829abd..fdf72a5009e 100644 --- a/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js +++ b/server/sonar-web/src/main/js/apps/organizations/navigation/OrganizationNavigation.js @@ -154,6 +154,13 @@ export default class OrganizationNavigation extends React.Component { {translate('organization.members.page')} </Link> </li> + <li> + <Link + to={`/organizations/${organization.key}/quality_profiles`} + activeClassName="active"> + {translate('quality_profiles.page')} + </Link> + </li> {organization.canAdmin && this.renderAdministration()} </ul> </div> diff --git a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap index 8fbbcd9b6a3..46358abc847 100644 --- a/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap +++ b/server/sonar-web/src/main/js/apps/organizations/navigation/__tests__/__snapshots__/OrganizationNavigation-test.js.snap @@ -44,6 +44,15 @@ exports[`test admin 1`] = ` organization.members.page </Link> </li> + <li> + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo/quality_profiles"> + quality_profiles.page + </Link> + </li> <li className=""> <a @@ -165,6 +174,15 @@ exports[`test regular user 1`] = ` organization.members.page </Link> </li> + <li> + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo/quality_profiles"> + quality_profiles.page + </Link> + </li> </ul> </div> </div> @@ -217,6 +235,15 @@ exports[`test undeletable org 1`] = ` organization.members.page </Link> </li> + <li> + <Link + activeClassName="active" + onlyActiveOnIndex={false} + style={Object {}} + to="/organizations/foo/quality_profiles"> + quality_profiles.page + </Link> + </li> <li className=""> <a diff --git a/server/sonar-web/src/main/js/apps/organizations/routes.js b/server/sonar-web/src/main/js/apps/organizations/routes.js index eae57052d3e..1b7f81eda3d 100644 --- a/server/sonar-web/src/main/js/apps/organizations/routes.js +++ b/server/sonar-web/src/main/js/apps/organizations/routes.js @@ -28,6 +28,7 @@ import OrganizationPermissions from './components/OrganizationPermissions'; import OrganizationPermissionTemplates from './components/OrganizationPermissionTemplates'; import OrganizationProjectsManagement from './components/OrganizationProjectsManagement'; import OrganizationDelete from './components/OrganizationDelete'; +import qualityProfilesRoutes from '../quality-profiles/routes'; const routes = [ { @@ -55,6 +56,10 @@ const routes = [ component: OrganizationMembersContainer }, { + path: 'quality_profiles', + childRoutes: qualityProfilesRoutes + }, + { component: OrganizationAdmin, childRoutes: [ { path: 'delete', component: OrganizationDelete }, diff --git a/server/sonar-web/src/main/js/apps/overview/meta/Meta.js b/server/sonar-web/src/main/js/apps/overview/meta/Meta.js index 9f67196b368..a18e1385f1b 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/Meta.js +++ b/server/sonar-web/src/main/js/apps/overview/meta/Meta.js @@ -57,7 +57,12 @@ const Meta = ({ component, measures, areThereCustomOrganizations }) => { {shouldShowQualityGate && <MetaQualityGate gate={qualityGate} />} - {shouldShowQualityProfiles && <MetaQualityProfiles profiles={qualityProfiles} />} + {shouldShowQualityProfiles && + <MetaQualityProfiles + component={component} + customOrganizations={areThereCustomOrganizations} + profiles={qualityProfiles} + />} <MetaLinks component={component} /> diff --git a/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.js b/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.js index ff4c39b3db0..fb5c176df85 100644 --- a/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.js +++ b/server/sonar-web/src/main/js/apps/overview/meta/MetaQualityProfiles.js @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import { connect } from 'react-redux'; import { Link } from 'react-router'; @@ -27,6 +28,15 @@ import { searchRules } from '../../../api/rules'; import { getLanguages } from '../../../store/rootReducer'; class MetaQualityProfiles extends React.Component { + mounted: boolean; + + props: { + component: { organization: string }, + customOrganizations: boolean, + languages: { [string]: { name: string } }, + profiles: Array<{ key: string, language: string, name: string }> + }; + state = { deprecatedByKey: {} }; @@ -74,12 +84,16 @@ class MetaQualityProfiles extends React.Component { const languageFromStore = this.props.languages[profile.language]; const languageName = languageFromStore ? languageFromStore.name : profile.language; + const path = this.props.customOrganizations + ? getQualityProfileUrl(profile.key, this.props.component.organization) + : getQualityProfileUrl(profile.key); + const inner = ( <div className="text-ellipsis"> <span className="note spacer-right"> {'(' + languageName + ')'} </span> - <Link to={getQualityProfileUrl(profile.key)}> + <Link to={path}> {profile.name} </Link> </div> diff --git a/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/QualityProfiles.js b/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/QualityProfiles.js index 88d4c79c5c7..ee3d6a0e774 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/QualityProfiles.js +++ b/server/sonar-web/src/main/js/apps/project-admin/quality-profiles/QualityProfiles.js @@ -17,36 +17,42 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import { connect } from 'react-redux'; -import shallowCompare from 'react-addons-shallow-compare'; import Header from './Header'; import Table from './Table'; import { fetchProjectProfiles, setProjectProfile } from '../store/actions'; import { + areThereCustomOrganizations, getProjectAdminAllProfiles, getProjectAdminProjectProfiles, getComponent } from '../../../store/rootReducer'; -class QualityProfiles extends React.Component { - static propTypes = { - component: React.PropTypes.object.isRequired, - allProfiles: React.PropTypes.array, - profiles: React.PropTypes.array - }; +type Props = { + allProfiles: Array<{}>, + component: { key: string, organization: string }, + customOrganizations: boolean, + fetchProjectProfiles: (componentKey: string, organization?: string) => void, + profiles: Array<{}>, + setProjectProfile: (string, string, string) => void +}; - componentDidMount() { - this.props.fetchProjectProfiles(this.props.component.key); - } +class QualityProfiles extends React.PureComponent { + props: Props; - shouldComponentUpdate(nextProps, nextState) { - return shallowCompare(this, nextProps, nextState); + componentDidMount() { + if (this.props.customOrganizations) { + this.props.fetchProjectProfiles(this.props.component.key, this.props.component.organization); + } else { + this.props.fetchProjectProfiles(this.props.component.key); + } } - handleChangeProfile(oldKey, newKey) { + handleChangeProfile = (oldKey, newKey) => { this.props.setProjectProfile(this.props.component.key, oldKey, newKey); - } + }; render() { const { allProfiles, profiles } = this.props; @@ -59,7 +65,7 @@ class QualityProfiles extends React.Component { ? <Table allProfiles={allProfiles} profiles={profiles} - onChangeProfile={this.handleChangeProfile.bind(this)} + onChangeProfile={this.handleChangeProfile} /> : <i className="spinner" />} </div> @@ -69,10 +75,11 @@ class QualityProfiles extends React.Component { const mapStateToProps = (state, ownProps) => ({ component: getComponent(state, ownProps.location.query.id), + customOrganizations: areThereCustomOrganizations(state), allProfiles: getProjectAdminAllProfiles(state), profiles: getProjectAdminProjectProfiles(state, ownProps.location.query.id) }); -export default connect(mapStateToProps, { fetchProjectProfiles, setProjectProfile })( - QualityProfiles -); +const mapDispatchToProps = { fetchProjectProfiles, setProjectProfile }; + +export default connect(mapStateToProps, mapDispatchToProps)(QualityProfiles); diff --git a/server/sonar-web/src/main/js/apps/project-admin/store/actions.js b/server/sonar-web/src/main/js/apps/project-admin/store/actions.js index 2f96a8bf723..d3f66e9af6c 100644 --- a/server/sonar-web/src/main/js/apps/project-admin/store/actions.js +++ b/server/sonar-web/src/main/js/apps/project-admin/store/actions.js @@ -47,9 +47,14 @@ export const receiveProjectProfiles = (projectKey, profiles) => ({ profiles }); -export const fetchProjectProfiles = projectKey => +export const fetchProjectProfiles = (projectKey, organization) => dispatch => { - Promise.all([getQualityProfiles(), getQualityProfiles({ projectKey })]).then(responses => { + Promise.all([ + organization ? getQualityProfiles({ organization }) : getQualityProfiles(), + organization + ? getQualityProfiles({ organization, projectKey }) + : getQualityProfiles({ projectKey }) + ]).then(responses => { const [allProfiles, projectProfiles] = responses; dispatch(receiveProfiles(allProfiles)); dispatch(receiveProjectProfiles(projectKey, projectProfiles)); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/Changelog.js b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/Changelog.js index cd70278214d..2e84e64d2a3 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/Changelog.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/Changelog.js @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import { Link } from 'react-router'; import moment from 'moment'; @@ -24,10 +25,19 @@ import ChangesList from './ChangesList'; import { translate } from '../../../helpers/l10n'; import { getRulesUrl } from '../../../helpers/urls'; -export default class Changelog extends React.Component { - static propTypes = { - events: React.PropTypes.array.isRequired - }; +type Props = { + events: Array<{ + action: string, + authorName: string, + date: string, + params?: {}, + ruleKey: string, + ruleName: string + }> +}; + +export default class Changelog extends React.PureComponent { + props: Props; render() { let isEvenRow = false; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.js index 8de4383ca42..c7ea2d89572 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogContainer.js @@ -17,40 +17,52 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import Changelog from './Changelog'; import ChangelogSearch from './ChangelogSearch'; import ChangelogEmpty from './ChangelogEmpty'; import { getProfileChangelog } from '../../../api/quality-profiles'; -import { ProfileType } from '../propTypes'; import { translate } from '../../../helpers/l10n'; - -export default class ChangelogContainer extends React.Component { - static propTypes = { - location: React.PropTypes.object.isRequired, - profile: ProfileType - }; +import { getProfileChangelogPath } from '../utils'; +import type { Profile } from '../propTypes'; + +type Props = { + location: { + query: { + since?: string, + to?: string + } + }, + organization: ?string, + profile: Profile +}; + +type State = { + events?: Array<*>, + loading: boolean, + page?: number, + total?: number +}; + +export default class ChangelogContainer extends React.PureComponent { + mounted: boolean; + props: Props; static contextTypes = { router: React.PropTypes.object }; - state = { + state: State = { loading: true }; - componentWillMount() { - this.handleFromDateChange = this.handleFromDateChange.bind(this); - this.handleToDateChange = this.handleToDateChange.bind(this); - this.handleReset = this.handleReset.bind(this); - } - componentDidMount() { this.mounted = true; this.loadChangelog(); } - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps: Props) { if (prevProps.location !== this.props.location) { this.loadChangelog(); } @@ -63,7 +75,7 @@ export default class ChangelogContainer extends React.Component { loadChangelog() { this.setState({ loading: true }); const { query } = this.props.location; - const data = { profileKey: this.props.profile.key }; + const data: Object = { profileKey: this.props.profile.key }; if (query.since) { data.since = query.since; } @@ -83,13 +95,13 @@ export default class ChangelogContainer extends React.Component { }); } - loadMore(e) { + loadMore(e: SyntheticInputEvent) { e.preventDefault(); e.target.blur(); this.setState({ loading: true }); const { query } = this.props.location; - const data = { + const data: Object = { profileKey: this.props.profile.key, p: this.state.page + 1 }; @@ -101,7 +113,7 @@ export default class ChangelogContainer extends React.Component { } getProfileChangelog(data).then(r => { - if (this.mounted) { + if (this.mounted && this.state.events) { this.setState({ events: [...this.state.events, ...r.events], total: r.total, @@ -112,25 +124,32 @@ export default class ChangelogContainer extends React.Component { }); } - handleFromDateChange(fromDate) { - const query = { ...this.props.location.query, since: fromDate }; - this.context.router.push({ pathname: '/profiles/changelog', query }); - } + handleFromDateChange = (fromDate?: string) => { + const path = getProfileChangelogPath(this.props.profile.key, this.props.organization, { + since: fromDate, + to: this.props.location.query.to + }); + this.context.router.push(path); + }; - handleToDateChange(toDate) { - const query = { ...this.props.location.query, to: toDate }; - this.context.router.push({ pathname: '/profiles/changelog', query }); - } + handleToDateChange = (toDate?: string) => { + const path = getProfileChangelogPath(this.props.profile.key, this.props.organization, { + since: this.props.location.query.since, + to: toDate + }); + this.context.router.push(path); + }; - handleReset() { - const query = { key: this.props.profile.key }; - this.context.router.push({ pathname: '/profiles/changelog', query }); - } + handleReset = () => { + const path = getProfileChangelogPath(this.props.profile.key, this.props.organization); + this.context.router.push(path); + }; render() { const { query } = this.props.location; const shouldDisplayFooter = this.state.events != null && + this.state.total != null && this.state.events.length < this.state.total; return ( diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogEmpty.js b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogEmpty.js index 1baf1d73e4f..96db0d8c8eb 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogEmpty.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogEmpty.js @@ -17,10 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import { translate } from '../../../helpers/l10n'; -export default class ChangelogEmpty extends React.Component { +export default class ChangelogEmpty extends React.PureComponent { render() { return ( <div className="big-spacer-top"> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogSearch.js b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogSearch.js index 1106ba94b34..89d2099a229 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogSearch.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangelogSearch.js @@ -17,20 +17,23 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import DateInput from '../../../components/controls/DateInput'; import { translate } from '../../../helpers/l10n'; -export default class ChangelogSearch extends React.Component { - static propTypes = { - fromDate: React.PropTypes.string, - toDate: React.PropTypes.string, - onFromDateChange: React.PropTypes.func.isRequired, - onToDateChange: React.PropTypes.func.isRequired, - onReset: React.PropTypes.func.isRequired - }; +type Props = { + fromDate?: string, + toDate?: string, + onFromDateChange: () => void, + onReset: () => void, + onToDateChange: () => void +}; - handleResetClick(e) { +export default class ChangelogSearch extends React.PureComponent { + props: Props; + + handleResetClick(e: SyntheticInputEvent) { e.preventDefault(); e.target.blur(); this.props.onReset(); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangesList.js b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangesList.js index 9ec1807227a..6377b4f3799 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangesList.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ChangesList.js @@ -17,14 +17,17 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import SeverityChange from './SeverityChange'; import ParameterChange from './ParameterChange'; -export default class ChangesList extends React.Component { - static propTypes = { - changes: React.PropTypes.object.isRequired - }; +type Props = { + changes: { [string]: ?string } +}; + +export default class ChangesList extends React.PureComponent { + props: Props; render() { const { changes } = this.props; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ParameterChange.js b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ParameterChange.js index 7b4e0168979..d4e01d55b72 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ParameterChange.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/ParameterChange.js @@ -17,14 +17,17 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import { translateWithParameters } from '../../../helpers/l10n'; -export default class ParameterChange extends React.Component { - static propTypes = { - name: React.PropTypes.string.isRequired, - value: React.PropTypes.any - }; +type Props = { + name: string, + value: ?string +}; + +export default class ParameterChange extends React.PureComponent { + props: Props; render() { const { name, value } = this.props; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/SeverityChange.js b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/SeverityChange.js index c6e20846d83..5497d9ad0fb 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/changelog/SeverityChange.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/changelog/SeverityChange.js @@ -17,14 +17,17 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import SeverityHelper from '../../../components/shared/severity-helper'; import { translate } from '../../../helpers/l10n'; -export default class SeverityChange extends React.Component { - static propTypes = { - severity: React.PropTypes.string.isRequired - }; +type Props = { + severity: ?string +}; + +export default class SeverityChange extends React.PureComponent { + props: Props; render() { return ( diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.js index a41bc66c8ad..cdc70a050ed 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonContainer.js @@ -17,28 +17,42 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import ComparisonForm from './ComparisonForm'; import ComparisonResults from './ComparisonResults'; -import { ProfileType, ProfilesListType } from '../propTypes'; import { compareProfiles } from '../../../api/quality-profiles'; +import { getProfileComparePath } from '../utils'; +import type { Profile } from '../propTypes'; -export default class ComparisonContainer extends React.Component { - static propTypes = { - profile: ProfileType, - profiles: ProfilesListType - }; +type Props = { + location: { query: { withKey?: string } }, + organization: ?string, + profile: Profile, + profiles: Array<Profile> +}; + +type State = { + loading: boolean, + left?: { name: string }, + right?: { name: string }, + inLeft?: Array<*>, + inRight?: Array<*>, + modified?: Array<*> +}; + +export default class ComparisonContainer extends React.PureComponent { + mounted: boolean; + props: Props; + state: State; static contextTypes = { router: React.PropTypes.object }; - state = { - loading: false - }; - - componentWillMount() { - this.handleCompare = this.handleCompare.bind(this); + constructor(props: Props) { + super(props); + this.state = { loading: false }; } componentDidMount() { @@ -46,7 +60,7 @@ export default class ComparisonContainer extends React.Component { this.loadResults(); } - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps: Props) { if (prevProps.profile !== this.props.profile || prevProps.location !== this.props.location) { this.loadResults(); } @@ -59,7 +73,7 @@ export default class ComparisonContainer extends React.Component { loadResults() { const { withKey } = this.props.location.query; if (!withKey) { - this.setState({ left: null, loading: false }); + this.setState({ left: undefined, loading: false }); return; } @@ -78,15 +92,10 @@ export default class ComparisonContainer extends React.Component { }); } - handleCompare(withKey) { - this.context.router.push({ - pathname: '/profiles/compare', - query: { - key: this.props.profile.key, - withKey - } - }); - } + handleCompare = (withKey: string) => { + const path = getProfileComparePath(this.props.profile.key, this.props.organization, withKey); + this.context.router.push(path); + }; render() { const { profile, profiles, location } = this.props; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonEmpty.js b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonEmpty.js index 7d7298dc761..b3a19a37f5c 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonEmpty.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonEmpty.js @@ -17,10 +17,11 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import { translate } from '../../../helpers/l10n'; -export default class ComparisonEmpty extends React.Component { +export default class ComparisonEmpty extends React.PureComponent { render() { return ( <div className="big-spacer-top"> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonForm.js b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonForm.js index f686b415b5a..a99f64b2186 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonForm.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonForm.js @@ -17,19 +17,23 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import Select from 'react-select'; -import { ProfileType, ProfilesListType } from '../propTypes'; import { translate } from '../../../helpers/l10n'; +import type { Profile } from '../propTypes'; -export default class ComparisonForm extends React.Component { - static propTypes = { - profile: ProfileType.isRequired, - profiles: ProfilesListType.isRequired, - onCompare: React.PropTypes.func.isRequired - }; +type Props = { + profile: Profile, + profiles: Array<Profile>, + onCompare: (string) => void, + withKey: string +}; - handleChange(option) { +export default class ComparisonForm extends React.PureComponent { + props: Props; + + handleChange(option: { value: string }) { this.props.onCompare(option.value); } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.js b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.js index bd8793490d3..c6acf738546 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/compare/ComparisonResults.js @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import { Link } from 'react-router'; import ComparisonEmpty from './ComparisonEmpty'; @@ -24,20 +25,20 @@ import SeverityIcon from '../../../components/shared/severity-icon'; import { translateWithParameters } from '../../../helpers/l10n'; import { getRulesUrl } from '../../../helpers/urls'; -export default class ComparisonResults extends React.Component { - static propTypes = { - left: React.PropTypes.shape({ - name: React.PropTypes.string.isRequired - }).isRequired, - right: React.PropTypes.shape({ - name: React.PropTypes.string.isRequired - }).isRequired, - inLeft: React.PropTypes.array.isRequired, - inRight: React.PropTypes.array.isRequired, - modified: React.PropTypes.array.isRequired - }; +type Params = { [string]: string }; - renderRule(rule, severity) { +type Props = { + left: { name: string }, + right: { name: string }, + inLeft: Array<*>, + inRight: Array<*>, + modified: Array<*> +}; + +export default class ComparisonResults extends React.PureComponent { + props: Props; + + renderRule(rule: { key: string, name: string }, severity: string) { return ( <div> <SeverityIcon severity={severity} /> @@ -49,7 +50,7 @@ export default class ComparisonResults extends React.Component { ); } - renderParameters(params) { + renderParameters(params: Params) { if (!params) { return null; } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/App.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/App.js index f8fd41c66c5..982553d7993 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/App.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/App.js @@ -17,17 +17,36 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import { getQualityProfiles, getExporters } from '../../../api/quality-profiles'; import { sortProfiles } from '../utils'; +import type { Exporter } from '../propTypes'; import '../styles.css'; -export default class App extends React.Component { - state = { loading: true }; +type Props = { + children: React.Element<*>, + currentUser: { permissions: { global: Array<string> } }, + languages: Array<*>, + organization: { canAdmin?: boolean, key: string } | null +}; + +type State = { + loading: boolean, + exporters?: Array<Exporter>, + profiles?: Array<*> +}; + +export default class App extends React.PureComponent { + mounted: boolean; + props: Props; + state: State = { loading: true }; componentWillMount() { - document.querySelector('html').classList.add('dashboard-page'); - this.updateProfiles = this.updateProfiles.bind(this); + const html = document.querySelector('html'); + if (html) { + html.classList.add('dashboard-page'); + } } componentDidMount() { @@ -37,12 +56,21 @@ export default class App extends React.Component { componentWillUnmount() { this.mounted = false; - document.querySelector('html').classList.remove('dashboard-page'); + const html = document.querySelector('html'); + if (html) { + html.classList.remove('dashboard-page'); + } + } + + fetchProfiles() { + const { organization } = this.props; + const data = organization ? { organization: organization.key } : {}; + return getQualityProfiles(data); } loadData() { this.setState({ loading: true }); - Promise.all([getExporters(), getQualityProfiles()]).then(responses => { + Promise.all([getExporters(), this.fetchProfiles()]).then(responses => { if (this.mounted) { const [exporters, profiles] = responses; this.setState({ @@ -54,13 +82,13 @@ export default class App extends React.Component { }); } - updateProfiles() { - return getQualityProfiles().then(profiles => { + updateProfiles = () => { + return this.fetchProfiles().then(profiles => { if (this.mounted) { this.setState({ profiles: sortProfiles(profiles) }); } }); - } + }; renderChild() { if (this.state.loading) { @@ -69,13 +97,16 @@ export default class App extends React.Component { const finalLanguages = Object.values(this.props.languages); - const canAdmin = this.props.currentUser.permissions.global.includes('profileadmin'); + const canAdmin = this.props.organization + ? this.props.organization.canAdmin + : this.props.currentUser.permissions.global.includes('profileadmin'); return React.cloneElement(this.props.children, { profiles: this.state.profiles, languages: finalLanguages, exporters: this.state.exporters, updateProfiles: this.updateProfiles, + organization: this.props.organization ? this.props.organization.key : null, canAdmin }); } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.js index a19ddff98b6..b5ff40a9ace 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/AppContainer.js @@ -19,9 +19,14 @@ */ import { connect } from 'react-redux'; import App from './App'; -import { getLanguages, getCurrentUser } from '../../../store/rootReducer'; +import { getLanguages, getCurrentUser, getOrganizationByKey } from '../../../store/rootReducer'; -export default connect(state => ({ +const mapStateToProps = (state, ownProps) => ({ currentUser: getCurrentUser(state), - languages: getLanguages(state) -}))(App); + languages: getLanguages(state), + organization: ownProps.params.organizationKey + ? getOrganizationByKey(state, ownProps.params.organizationKey) + : null +}); + +export default connect(mapStateToProps)(App); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js index 1616c0936c8..1101c1560ce 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileActions.js @@ -17,78 +17,74 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import { Link } from 'react-router'; import RenameProfileView from '../views/RenameProfileView'; import CopyProfileView from '../views/CopyProfileView'; import DeleteProfileView from '../views/DeleteProfileView'; import { translate } from '../../../helpers/l10n'; -import { ProfileType } from '../propTypes'; import { getRulesUrl } from '../../../helpers/urls'; import { setDefaultProfile } from '../../../api/quality-profiles'; +import { getProfilePath, getProfileComparePath, getProfilesPath } from '../utils'; +import type { Profile } from '../propTypes'; -export default class ProfileActions extends React.Component { - static propTypes = { - profile: ProfileType.isRequired, - canAdmin: React.PropTypes.bool.isRequired, - updateProfiles: React.PropTypes.func.isRequired - }; +type Props = { + canAdmin: boolean, + organization: ?string, + profile: Profile, + updateProfiles: () => Promise<*> +}; + +export default class ProfileActions extends React.PureComponent { + props: Props; static contextTypes = { router: React.PropTypes.object }; - handleRenameClick(e) { + handleRenameClick = (e: SyntheticInputEvent) => { e.preventDefault(); - new RenameProfileView({ - profile: this.props.profile - }) - .on('done', () => { - this.props.updateProfiles(); - }) + new RenameProfileView({ profile: this.props.profile }) + .on('done', () => this.props.updateProfiles()) .render(); - } + }; - handleCopyClick(e) { + handleCopyClick = (e: SyntheticInputEvent) => { e.preventDefault(); - new CopyProfileView({ - profile: this.props.profile - }) + new CopyProfileView({ profile: this.props.profile }) .on('done', profile => { this.props.updateProfiles().then(() => { - this.context.router.push({ - pathname: '/profiles/show', - query: { key: profile.key } - }); + this.context.router.push(getProfilePath(profile.key, this.props.organization)); }); }) .render(); - } + }; - handleSetDefaultClick(e) { + handleSetDefaultClick = (e: SyntheticInputEvent) => { e.preventDefault(); setDefaultProfile(this.props.profile.key).then(this.props.updateProfiles); - } + }; - handleDeleteClick(e) { + handleDeleteClick = (e: SyntheticInputEvent) => { e.preventDefault(); - new DeleteProfileView({ - profile: this.props.profile - }) + new DeleteProfileView({ profile: this.props.profile }) .on('done', () => { - this.context.router.replace('/profiles'); + this.context.router.replace(getProfilesPath(this.props.organization)); this.props.updateProfiles(); }) .render(); - } + }; render() { const { profile, canAdmin } = this.props; + // FIXME use org, name and lang const backupUrl = window.baseUrl + '/api/qualityprofiles/backup?profileKey=' + encodeURIComponent(profile.key); + // FIXME getRulesUrl const activateMoreUrl = getRulesUrl({ qprofile: profile.key, activation: 'false' @@ -109,37 +105,34 @@ export default class ProfileActions extends React.Component { </li> <li> <Link - to={{ pathname: '/profiles/compare', query: { key: profile.key } }} + to={getProfileComparePath(profile.key, this.props.organization)} id="quality-profile-compare"> {translate('compare')} </Link> </li> {canAdmin && <li> - <a id="quality-profile-copy" href="#" onClick={this.handleCopyClick.bind(this)}> + <a id="quality-profile-copy" href="#" onClick={this.handleCopyClick}> {translate('copy')} </a> </li>} {canAdmin && <li> - <a id="quality-profile-rename" href="#" onClick={this.handleRenameClick.bind(this)}> + <a id="quality-profile-rename" href="#" onClick={this.handleRenameClick}> {translate('rename')} </a> </li>} {canAdmin && !profile.isDefault && <li> - <a - id="quality-profile-set-as-default" - href="#" - onClick={this.handleSetDefaultClick.bind(this)}> + <a id="quality-profile-set-as-default" href="#" onClick={this.handleSetDefaultClick}> {translate('set_as_default')} </a> </li>} {canAdmin && !profile.isDefault && <li> - <a id="quality-profile-delete" href="#" onClick={this.handleDeleteClick.bind(this)}> + <a id="quality-profile-delete" href="#" onClick={this.handleDeleteClick}> {translate('delete')} </a> </li>} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js index e11cbe0968c..d55f57dd8fe 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileContainer.js @@ -17,31 +17,41 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import Helmet from 'react-helmet'; import ProfileNotFound from './ProfileNotFound'; import ProfileHeader from '../details/ProfileHeader'; import { translate } from '../../../helpers/l10n'; -import { ProfilesListType } from '../propTypes'; +import type { Profile } from '../propTypes'; -export default class ProfileContainer extends React.Component { - static propTypes = { - location: React.PropTypes.object, - profiles: ProfilesListType, - canAdmin: React.PropTypes.bool, - updateProfiles: React.PropTypes.func - }; +type Props = { + canAdmin: boolean, + children: React.Element<*>, + location: { query: { key: string } }, + organization: ?string, + profiles: Array<Profile>, + updateProfiles: () => Promise<*> +}; + +export default class ProfileContainer extends React.PureComponent { + props: Props; render() { - const { profiles, location, ...other } = this.props; + const { organization, profiles, location, ...other } = this.props; const { key } = location.query; const profile = profiles.find(profile => profile.key === key); if (!profile) { - return <ProfileNotFound />; + return <ProfileNotFound organization={organization} />; } - const child = React.cloneElement(this.props.children, { profile, profiles, ...other }); + const child = React.cloneElement(this.props.children, { + organization, + profile, + profiles, + ...other + }); const title = translate('quality_profiles.page') + ' - ' + profile.name; @@ -50,8 +60,9 @@ export default class ProfileContainer extends React.Component { <Helmet title={title} titleTemplate="SonarQube - %s" /> <ProfileHeader - profile={profile} canAdmin={this.props.canAdmin} + organization={organization} + profile={profile} updateProfiles={this.props.updateProfiles} /> {child} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileDate.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileDate.js index 369a6a067fa..158e6b9955b 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileDate.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileDate.js @@ -17,18 +17,21 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import moment from 'moment'; import shallowCompare from 'react-addons-shallow-compare'; import { translate } from '../../../helpers/l10n'; -export default class ProfileDate extends React.Component { - static propTypes = { - date: React.PropTypes.string - }; +type Props = { + date?: string +}; - shouldComponentUpdate(nextProps, nextState) { - return shallowCompare(this, nextProps, nextState); +export default class ProfileDate extends React.PureComponent { + props: Props; + + shouldComponentUpdate(nextProps: Props) { + return shallowCompare(this, nextProps); } render() { diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileLink.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileLink.js index e6d332735b7..a60c37ab9b1 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileLink.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileLink.js @@ -17,20 +17,25 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import { Link } from 'react-router'; +import { getProfilePath } from '../utils'; -export default class ProfileLink extends React.Component { - static propTypes = { - profileKey: React.PropTypes.string.isRequired - }; +type Props = { + children?: React.Element<*>, + organization: ?string, + profileKey: string +}; + +export default class ProfileLink extends React.PureComponent { + props: Props; render() { - const { profileKey, children, ...other } = this.props; - const query = { key: profileKey }; + const { profileKey, organization, children, ...other } = this.props; return ( <Link - to={{ pathname: '/profiles/show', query }} + to={getProfilePath(profileKey, organization)} activeClassName="link-no-underline" {...other}> {children} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileNotFound.js b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileNotFound.js index 2dff3d655e0..7401007841d 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileNotFound.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/components/ProfileNotFound.js @@ -17,16 +17,24 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import { IndexLink } from 'react-router'; import { translate } from '../../../helpers/l10n'; +import { getProfilesPath } from '../utils'; + +type Props = { + organization: ?string +}; + +export default class ProfileNotFound extends React.PureComponent { + props: Props; -export default class ProfileNotFound extends React.Component { render() { return ( <div className="quality-profile-not-found"> <div className="note spacer-bottom"> - <IndexLink to="/profiles/" className="text-muted"> + <IndexLink to={getProfilesPath(this.props.organization)} className="text-muted"> {translate('quality_profiles.page')} </IndexLink> </div> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.js index b6deaa8ddc7..7b7a296536c 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileDetails.js @@ -17,19 +17,25 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import ProfileRules from './ProfileRules'; import ProfileProjects from './ProfileProjects'; import ProfileInheritance from './ProfileInheritance'; import ProfileExporters from './ProfileExporters'; -import { ProfileType } from '../propTypes'; +import type { Profile, Exporter } from '../propTypes'; -export default class ProfileDetails extends React.Component { - static propTypes = { - profile: ProfileType, - canAdmin: React.PropTypes.bool, - updateProfiles: React.PropTypes.func - }; +type Props = { + canAdmin: boolean, + exporters: Array<Exporter>, + organization: ?string, + profile: Profile, + profiles: Array<Profile>, + updateProfiles: () => Promise<*> +}; + +export default class ProfileDetails extends React.PureComponent { + props: Props; render() { return ( diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileExporters.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileExporters.js index 1a7e9df653f..be6702dd7ed 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileExporters.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileExporters.js @@ -17,23 +17,34 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow +import { stringify } from 'querystring'; import React from 'react'; import { translate } from '../../../helpers/l10n'; +import type { Profile, Exporter } from '../propTypes'; -export default class ProfileExporters extends React.Component { - static propTypes = { - exporters: React.PropTypes.array.isRequired - }; +type Props = { + exporters: Array<Exporter>, + organization: ?string, + profile: Profile +}; - getExportUrl(exporter) { - return window.baseUrl + - '/api/qualityprofiles/export' + - '?exporterKey=' + - encodeURIComponent(exporter.key) + - '&language=' + - encodeURIComponent(this.props.profile.language) + - '&name=' + - encodeURIComponent(this.props.profile.name); +export default class ProfileExporters extends React.PureComponent { + props: Props; + + getExportUrl(exporter: Exporter) { + const { organization, profile } = this.props; + + const path = '/api/qualityprofiles/export'; + const parameters: { [string]: string } = { + exporterKey: exporter.key, + language: profile.language, + name: profile.name + }; + if (organization) { + Object.assign(parameters, { organization }); + } + return window.baseUrl + path + '?' + stringify(parameters); } render() { diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.js index a581dad1983..3a06c63e472 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileHeader.js @@ -17,21 +17,30 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import { Link, IndexLink } from 'react-router'; import ProfileLink from '../components/ProfileLink'; import ProfileActions from '../components/ProfileActions'; import ProfileDate from '../components/ProfileDate'; -import { ProfileType } from '../propTypes'; import { translate } from '../../../helpers/l10n'; -import { isStagnant } from '../utils'; +import { + isStagnant, + getProfilesPath, + getProfilesForLanguagePath, + getProfileChangelogPath +} from '../utils'; +import type { Profile } from '../propTypes'; -export default class ProfileHeader extends React.Component { - static propTypes = { - profile: ProfileType.isRequired, - canAdmin: React.PropTypes.bool.isRequired, - updateProfiles: React.PropTypes.func.isRequired - }; +type Props = { + canAdmin: boolean, + organization: ?string, + profile: Profile, + updateProfiles: () => Promise<*> +}; + +export default class ProfileHeader extends React.PureComponent { + props: Props; renderUpdateDate() { const { profile } = this.props; @@ -81,25 +90,28 @@ export default class ProfileHeader extends React.Component { } render() { - const { profile } = this.props; + const { organization, profile } = this.props; return ( <header className="page-header quality-profile-header"> <div className="note spacer-bottom"> - <IndexLink to="/profiles/" className="text-muted"> + <IndexLink to={getProfilesPath(organization)} className="text-muted"> {translate('quality_profiles.page')} </IndexLink> {' / '} <Link - to={{ pathname: '/profiles/', query: { language: profile.language } }} + to={getProfilesForLanguagePath(profile.language, organization)} className="text-muted"> {profile.languageName} </Link> </div> <h1 className="page-title"> - <ProfileLink profileKey={profile.key} className="link-base-color"> - {profile.name} + <ProfileLink + organization={organization} + profileKey={profile.key} + className="link-base-color"> + <span>{profile.name}</span> </ProfileLink> </h1> @@ -108,9 +120,7 @@ export default class ProfileHeader extends React.Component { {this.renderUpdateDate()} {this.renderUsageDate()} <li> - <Link - to={{ pathname: '/profiles/changelog', query: { key: profile.key } }} - className="button"> + <Link to={getProfileChangelogPath(profile.key, organization)} className="button"> {translate('changelog')} </Link> </li> @@ -122,8 +132,9 @@ export default class ProfileHeader extends React.Component { <i className="icon-dropdown" /> </button> <ProfileActions - profile={profile} canAdmin={this.props.canAdmin} + organization={organization} + profile={profile} updateProfiles={this.props.updateProfiles} /> </div> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.js index d17198f89d5..f6760f674f5 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritance.js @@ -17,34 +17,58 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import classNames from 'classnames'; import ProfileInheritanceBox from './ProfileInheritanceBox'; import ChangeParentView from '../views/ChangeParentView'; -import { ProfileType } from '../propTypes'; import { translate } from '../../../helpers/l10n'; import { getProfileInheritance } from '../../../api/quality-profiles'; +import type { Profile } from '../propTypes'; -export default class ProfileInheritance extends React.Component { - static propTypes = { - profile: ProfileType.isRequired, - canAdmin: React.PropTypes.bool.isRequired - }; +type Props = { + canAdmin: boolean, + organization: ?string, + profile: Profile, + profiles: Array<Profile>, + updateProfiles: () => Promise<*> +}; + +type State = { + ancestors?: Array<{ + activeRuleCount: number, + key: string, + name: string, + overridingRuleCount?: number + }>, + children?: Array<{ + activeRuleCount: number, + key: string, + name: string, + overridingRuleCount?: number + }>, + loading: boolean, + profile?: { + activeRuleCount: number, + key: string, + name: string, + overridingRuleCount?: number + } +}; - state = { +export default class ProfileInheritance extends React.PureComponent { + mounted: boolean; + props: Props; + state: State = { loading: true }; - componentWillMount() { - this.handleChangeParent = this.handleChangeParent.bind(this); - } - componentDidMount() { this.mounted = true; this.loadData(); } - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps: Props) { if (prevProps.profile !== this.props.profile) { this.loadData(); } @@ -68,20 +92,17 @@ export default class ProfileInheritance extends React.Component { }); } - handleChangeParent(e) { + handleChangeParent = (e: SyntheticInputEvent) => { e.preventDefault(); - new ChangeParentView({ - profile: this.props.profile, - profiles: this.props.profiles - }) - .on('done', () => { - this.props.updateProfiles(); - }) + new ChangeParentView({ profile: this.props.profile, profiles: this.props.profiles }) + .on('done', () => this.props.updateProfiles()) .render(); - } + }; render() { const highlightCurrent = !this.state.loading && + this.state.ancestors != null && + this.state.children != null && (this.state.ancestors.length > 0 || this.state.children.length > 0); const currentClassName = classNames('js-inheritance-current', { selected: highlightCurrent @@ -102,30 +123,35 @@ export default class ProfileInheritance extends React.Component { {!this.state.loading && <table className="data zebra"> <tbody> - {this.state.ancestors.map((ancestor, index) => ( - <ProfileInheritanceBox - key={ancestor.key} - profile={ancestor} - depth={index} - className="js-inheritance-ancestor" - /> - ))} + {this.state.ancestors != null && + this.state.ancestors.map((ancestor, index) => ( + <ProfileInheritanceBox + className="js-inheritance-ancestor" + depth={index} + key={ancestor.key} + organization={this.props.organization} + profile={ancestor} + /> + ))} <ProfileInheritanceBox - profile={this.state.profile} - depth={this.state.ancestors.length} - displayLink={false} className={currentClassName} + depth={this.state.ancestors ? this.state.ancestors.length : 0} + displayLink={false} + organization={this.props.organization} + profile={this.state.profile} /> - {this.state.children.map(child => ( - <ProfileInheritanceBox - key={child.key} - profile={child} - depth={this.state.ancestors.length + 1} - className="js-inheritance-child" - /> - ))} + {this.state.children != null && + this.state.children.map(child => ( + <ProfileInheritanceBox + className="js-inheritance-child" + depth={this.state.ancestors ? this.state.ancestors.length + 1 : 0} + key={child.key} + organization={this.props.organization} + profile={child} + /> + ))} </tbody> </table>} </div> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritanceBox.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritanceBox.js index 7e8b464ad16..5af9d91b207 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritanceBox.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileInheritanceBox.js @@ -17,22 +17,26 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import ProfileLink from '../components/ProfileLink'; import { translateWithParameters } from '../../../helpers/l10n'; -export default class ProfileInheritanceBox extends React.Component { - static propTypes = { - profile: React.PropTypes.shape({ - key: React.PropTypes.string.isRequired, - name: React.PropTypes.string.isRequired, - activeRuleCount: React.PropTypes.number.isRequired, - overridingRuleCount: React.PropTypes.number - }).isRequired, - depth: React.PropTypes.number.isRequired, - displayLink: React.PropTypes.bool, - className: React.PropTypes.string - }; +type Props = { + className?: string, + depth: number, + displayLink?: boolean, + organization: ?string, + profile: { + activeRuleCount: number, + key: string, + name: string, + overridingRuleCount?: number + } +}; + +export default class ProfileInheritanceBox extends React.PureComponent { + props: Props; static defaultProps = { displayLink: true @@ -47,7 +51,7 @@ export default class ProfileInheritanceBox extends React.Component { <td> <div style={{ paddingLeft: offset }}> {this.props.displayLink - ? <ProfileLink profileKey={profile.key}> + ? <ProfileLink organization={this.props.organization} profileKey={profile.key}> {profile.name} </ProfileLink> : profile.name} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.js index 650eae3746e..6d877d785fa 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileProjects.js @@ -17,34 +17,42 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import { Link } from 'react-router'; import ChangeProjectsView from '../views/ChangeProjectsView'; import QualifierIcon from '../../../components/shared/qualifier-icon'; -import { ProfileType } from '../propTypes'; import { getProfileProjects } from '../../../api/quality-profiles'; import { translate } from '../../../helpers/l10n'; - -export default class ProfileProjects extends React.Component { - static propTypes = { - profile: ProfileType, - canAdmin: React.PropTypes.bool.isRequired - }; - - state = { +import type { Profile } from '../propTypes'; + +type Props = { + canAdmin: boolean, + organization: ?string, + profile: Profile, + updateProfiles: () => Promise<*> +}; + +type State = { + loading: boolean, + more?: boolean, + projects: ?Array<*> +}; + +export default class ProfileProjects extends React.PureComponent { + mounted: boolean; + props: Props; + state: State = { + loading: true, projects: null }; - componentWillMount() { - this.loadProjects = this.loadProjects.bind(this); - } - componentDidMount() { this.mounted = true; this.loadProjects(); } - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps: Props) { if (prevProps.profile !== this.props.profile) { this.loadProjects(); } @@ -71,12 +79,13 @@ export default class ProfileProjects extends React.Component { }); } - handleChange(e) { + handleChange(e: SyntheticInputEvent) { e.preventDefault(); e.target.blur(); new ChangeProjectsView({ - profile: this.props.profile, - loadProjects: this.props.updateProfiles + loadProjects: this.props.updateProfiles, + organization: this.props.organization, + profile: this.props.profile }).render(); } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js index 2dee1b5680e..7a68617ab25 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRules.js @@ -17,26 +17,36 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import { Link } from 'react-router'; import { keyBy } from 'lodash'; import ProfileRulesRow from './ProfileRulesRow'; -import { ProfileType } from '../propTypes'; import { searchRules, takeFacet } from '../../../api/rules'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { formatMeasure } from '../../../helpers/measures'; import { getRulesUrl, getDeprecatedActiveRulesUrl } from '../../../helpers/urls'; import IssueTypeIcon from '../../../components/ui/IssueTypeIcon'; +import type { Profile } from '../propTypes'; const TYPES = ['BUG', 'VULNERABILITY', 'CODE_SMELL']; -export default class ProfileRules extends React.Component { - static propTypes = { - profile: ProfileType.isRequired, - canAdmin: React.PropTypes.bool.isRequired - }; - - state = { +type Props = { + canAdmin: boolean, + profile: Profile +}; + +type State = { + total: ?number, + activatedTotal: ?number, + allByType?: { [string]: ?{ val: string, count: ?number } }, + activatedByType?: { [string]: ?{ val: string, count: ?number } } +}; + +export default class ProfileRules extends React.PureComponent { + mounted: boolean; + props: Props; + state: State = { total: null, activatedTotal: null, allByType: keyBy(TYPES.map(t => ({ val: t, count: null })), 'val'), @@ -48,7 +58,7 @@ export default class ProfileRules extends React.Component { this.loadRules(); } - componentDidUpdate(prevProps) { + componentDidUpdate(prevProps: Props) { if (prevProps.profile !== this.props.profile) { this.loadRules(); } @@ -89,7 +99,7 @@ export default class ProfileRules extends React.Component { }); } - getTooltip(count, total) { + getTooltip(count: ?number, total: ?number) { if (count == null || total == null) { return ''; } @@ -101,10 +111,17 @@ export default class ProfileRules extends React.Component { ); } - getTooltipForType(type) { - const { count } = this.state.activatedByType[type]; - const total = this.state.allByType[type].count; - return this.getTooltip(count, total); + getTooltipForType(type: string) { + if ( + this.state.activatedByType && + this.state.activatedByType[type] && + this.state.allByType && + this.state.allByType[type] + ) { + const { count } = this.state.activatedByType[type]; + const total = this.state.allByType[type].count; + return this.getTooltip(count, total); + } } renderActiveTitle() { @@ -140,7 +157,7 @@ export default class ProfileRules extends React.Component { activation: 'false' }); - if (this.state.total == null) { + if (this.state.total == null || this.state.activatedTotal == null) { return null; } @@ -157,7 +174,7 @@ export default class ProfileRules extends React.Component { ); } - renderTitleForType(type) { + renderTitleForType(type: string) { return ( <span> <IssueTypeIcon query={type} className="little-spacer-right" /> @@ -166,14 +183,16 @@ export default class ProfileRules extends React.Component { ); } - renderCountForType(type) { + renderCountForType(type: string) { const rulesUrl = getRulesUrl({ qprofile: this.props.profile.key, activation: 'true', types: type }); - const { count } = this.state.activatedByType[type]; + const count = this.state.activatedByType && this.state.activatedByType[type] + ? this.state.activatedByType[type].count + : null; if (count == null) { return null; @@ -186,17 +205,22 @@ export default class ProfileRules extends React.Component { ); } - renderTotalForType(type) { + renderTotalForType(type: string) { const rulesUrl = getRulesUrl({ qprofile: this.props.profile.key, activation: 'false', types: type }); - const { count } = this.state.activatedByType[type]; - const { count: total } = this.state.allByType[type]; + const count = this.state.activatedByType && this.state.activatedByType[type] + ? this.state.activatedByType[type].count + : null; - if (count == null) { + const total = this.state.allByType && this.state.allByType[type] + ? this.state.allByType[type].count + : null; + + if (count == null || total == null) { return null; } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRow.js b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRow.js index ae74c615802..9a7d140fa14 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRow.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/details/ProfileRulesRow.js @@ -17,14 +17,17 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; -export default class ProfileRulesRow extends React.Component { - static propTypes = { - renderTitle: React.PropTypes.func.isRequired, - renderCount: React.PropTypes.func.isRequired, - renderTotal: React.PropTypes.func.isRequired - }; +type Props = { + renderCount: () => ?React.Element<*>, + renderTitle: () => React.Element<*>, + renderTotal: () => ?React.Element<*> +}; + +export default class ProfileRulesRow extends React.PureComponent { + props: Props; render() { const { renderTitle, renderCount, renderTotal } = this.props; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/Evolution.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/Evolution.js index b87f3ceb5df..23995d62e19 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/Evolution.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/Evolution.js @@ -17,22 +17,28 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import EvolutionDeprecated from './EvolutionDeprecated'; import EvolutionStagnant from './EvolutionStagnant'; import EvolutionRules from './EvolutionRules'; -import { ProfilesListType } from '../propTypes'; +import type { Profile } from '../propTypes'; -export default class Evolution extends React.Component { - static propTypes = { - profiles: ProfilesListType.isRequired - }; +type Props = { + organization: ?string, + profiles: Array<Profile> +}; + +export default class Evolution extends React.PureComponent { + props: Props; render() { + const { organization, profiles } = this.props; + return ( <div className="quality-profiles-evolution"> - <EvolutionDeprecated profiles={this.props.profiles} /> - <EvolutionStagnant profiles={this.props.profiles} /> + <EvolutionDeprecated organization={organization} profiles={profiles} /> + <EvolutionStagnant organization={organization} profiles={profiles} /> <EvolutionRules /> </div> ); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.js index 8c2a79cc697..ce8f4ddafbc 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionDeprecated.js @@ -17,20 +17,26 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import { Link } from 'react-router'; import { sortBy } from 'lodash'; import ProfileLink from '../components/ProfileLink'; import { getDeprecatedActiveRulesUrl } from '../../../helpers/urls'; -import { ProfilesListType } from '../propTypes'; import { translateWithParameters, translate } from '../../../helpers/l10n'; +import type { Profile } from '../propTypes'; -export default class EvolutionDeprecated extends React.Component { - static propTypes = { - profiles: ProfilesListType.isRequired - }; +type Props = { + organization: ?string, + profiles: Array<Profile> +}; + +export default class EvolutionDeprecated extends React.PureComponent { + props: Props; render() { + // FIXME getDeprecatedActiveRulesUrl + const profilesWithDeprecations = this.props.profiles.filter( profile => profile.activeDeprecatedRuleCount > 0 ); @@ -56,7 +62,10 @@ export default class EvolutionDeprecated extends React.Component { {sortedProfiles.map(profile => ( <li key={profile.key} className="spacer-top"> <div className="text-ellipsis"> - <ProfileLink profileKey={profile.key} className="link-no-underline"> + <ProfileLink + organization={this.props.organization} + profileKey={profile.key} + className="link-no-underline"> {profile.name} </ProfileLink> </div> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.js index 224be2a3b28..553e6bdf62c 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionRules.js @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import { Link } from 'react-router'; import moment from 'moment'; @@ -38,7 +39,11 @@ function parseRules(r) { }); } -export default class EvolutionRules extends React.Component { +type Props = {}; + +export default class EvolutionRules extends React.PureComponent { + mounted: boolean; + props: Props; state = {}; componentDidMount() { @@ -70,6 +75,8 @@ export default class EvolutionRules extends React.Component { } render() { + // FIXME getRulesUrl + if (!this.state.latestRulesTotal) { return null; } diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.js index 833ec05fd33..29679aca1ae 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/EvolutionStagnant.js @@ -17,17 +17,21 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import moment from 'moment'; import ProfileLink from '../components/ProfileLink'; -import { ProfilesListType } from '../propTypes'; import { translate } from '../../../helpers/l10n'; import { isStagnant } from '../utils'; +import type { Profile } from '../propTypes'; -export default class EvolutionStagnant extends React.Component { - static propTypes = { - profiles: ProfilesListType.isRequired - }; +type Props = { + organization: ?string, + profiles: Array<Profile> +}; + +export default class EvolutionStagnant extends React.PureComponent { + props: Props; render() { // TODO filter built-in out @@ -50,7 +54,10 @@ export default class EvolutionStagnant extends React.Component { {outdated.map(profile => ( <li key={profile.key} className="spacer-top"> <div className="text-ellipsis"> - <ProfileLink profileKey={profile.key} className="link-no-underline"> + <ProfileLink + organization={this.props.organization} + profileKey={profile.key} + className="link-no-underline"> {profile.name} </ProfileLink> </div> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js index e5979422006..e675d6245f9 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/HomeContainer.js @@ -17,14 +17,27 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import Helmet from 'react-helmet'; import PageHeader from './PageHeader'; import Evolution from './Evolution'; import ProfilesList from './ProfilesList'; import { translate } from '../../../helpers/l10n'; +import type { Profile } from '../propTypes'; + +type Props = { + canAdmin: boolean, + languages: Array<{ key: string, name: string }>, + location: { query: { [string]: string } }, + organization?: string, + profiles: Array<Profile>, + updateProfiles: () => Promise<*> +}; + +export default class HomeContainer extends React.PureComponent { + props: Props; -export default class HomeContainer extends React.Component { render() { return ( <div> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js index 9d4ca3dbefb..48ff0383758 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/PageHeader.js @@ -17,17 +17,25 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import CreateProfileView from '../views/CreateProfileView'; import RestoreProfileView from '../views/RestoreProfileView'; import RestoreBuiltInProfilesView from '../views/RestoreBuiltInProfilesView'; +import { getProfilePath } from '../utils'; import { translate } from '../../../helpers/l10n'; import { getImporters } from '../../../api/quality-profiles'; -export default class PageHeader extends React.Component { - static propTypes = { - canAdmin: React.PropTypes.bool.isRequired - }; +type Props = { + canAdmin: boolean, + languages: Array<{ key: string, name: string }>, + organization: ?string, + updateProfiles: () => Promise<*> +}; + +export default class PageHeader extends React.PureComponent { + mounted: boolean; + props: Props; static contextTypes = { router: React.PropTypes.object @@ -54,37 +62,42 @@ export default class PageHeader extends React.Component { } } - handleCreateClick(e) { + handleCreateClick = (e: SyntheticInputEvent) => { e.preventDefault(); e.target.blur(); this.retrieveImporters().then(importers => { new CreateProfileView({ languages: this.props.languages, + organization: this.props.organization, importers }) .on('done', profile => { this.props.updateProfiles().then(() => { - this.context.router.push({ - pathname: '/profiles/show', - query: { key: profile.key } - }); + this.context.router.push(getProfilePath(profile.key, this.props.organization)); }); }) .render(); }); - } + }; - handleRestoreClick(e) { + handleRestoreClick = (e: SyntheticInputEvent) => { e.preventDefault(); - new RestoreProfileView().on('done', this.props.updateProfiles).render(); - } + new RestoreProfileView({ + organization: this.props.organization + }) + .on('done', this.props.updateProfiles) + .render(); + }; - handleRestoreBuiltIn(e) { + handleRestoreBuiltIn = (e: SyntheticInputEvent) => { e.preventDefault(); - new RestoreBuiltInProfilesView({ languages: this.props.languages }) + new RestoreBuiltInProfilesView({ + languages: this.props.languages, + organization: this.props.organization + }) .on('done', this.props.updateProfiles) .render(); - } + }; render() { return ( @@ -95,7 +108,7 @@ export default class PageHeader extends React.Component { {this.props.canAdmin && <div className="page-actions button-group dropdown"> - <button id="quality-profiles-create" onClick={this.handleCreateClick.bind(this)}> + <button id="quality-profiles-create" onClick={this.handleCreateClick}> {translate('create')} </button> <button className="dropdown-toggle js-more-admin-actions" data-toggle="dropdown"> @@ -103,10 +116,7 @@ export default class PageHeader extends React.Component { </button> <ul className="dropdown-menu dropdown-menu-right"> <li> - <a - href="#" - id="quality-profiles-restore" - onClick={this.handleRestoreClick.bind(this)}> + <a href="#" id="quality-profiles-restore" onClick={this.handleRestoreClick}> {translate('quality_profiles.restore_profile')} </a> </li> @@ -115,7 +125,7 @@ export default class PageHeader extends React.Component { <a href="#" id="quality-profiles-restore-built-in" - onClick={this.handleRestoreBuiltIn.bind(this)}> + onClick={this.handleRestoreBuiltIn}> {translate('quality_profiles.restore_built_in_profiles')} </a> </li> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.js index 34d0317107f..c2b0681f686 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesList.js @@ -17,36 +17,46 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import { groupBy, pick, sortBy } from 'lodash'; import ProfilesListRow from './ProfilesListRow'; import ProfilesListHeader from './ProfilesListHeader'; -import { ProfilesListType, LanguagesListType } from '../propTypes'; import { translate, translateWithParameters } from '../../../helpers/l10n'; import { TooltipsContainer } from '../../../components/mixins/tooltips-mixin'; +import type { Profile } from '../propTypes'; -export default class ProfilesList extends React.Component { - static propTypes = { - profiles: ProfilesListType, - languages: LanguagesListType, - location: React.PropTypes.object, - canAdmin: React.PropTypes.bool.isRequired, - updateProfiles: React.PropTypes.func.isRequired - }; +type Props = { + canAdmin: boolean, + languages: Array<{ key: string, name: string }>, + location: { query: { [string]: string } }, + organization: ?string, + profiles: Array<Profile>, + updateProfiles: () => Promise<*> +}; - renderProfiles(profiles) { +export default class ProfilesList extends React.PureComponent { + props: Props; + + renderProfiles(profiles: Array<Profile>) { return profiles.map(profile => ( <ProfilesListRow + canAdmin={this.props.canAdmin} key={profile.key} + organization={this.props.organization} profile={profile} - canAdmin={this.props.canAdmin} updateProfiles={this.props.updateProfiles} /> )); } - renderHeader(languageKey, profilesCount) { + renderHeader(languageKey: string, profilesCount: number) { const language = this.props.languages.find(l => l.key === languageKey); + + if (!language) { + return null; + } + return ( <thead> <tr> @@ -84,7 +94,11 @@ export default class ProfilesList extends React.Component { return ( <div> - <ProfilesListHeader languages={languages} currentFilter={language} /> + <ProfilesListHeader + currentFilter={language} + languages={languages} + organization={this.props.organization} + /> {Object.keys(profilesToShow).length === 0 && <div className="alert alert-warning spacer-top"> @@ -95,11 +109,13 @@ export default class ProfilesList extends React.Component { <div key={languageKey} className="quality-profile-box quality-profiles-table"> <table data-language={languageKey} className="data zebra zebra-hover"> - {this.renderHeader(languageKey, profilesToShow[languageKey].length)} + {profilesToShow[languageKey] != null && + this.renderHeader(languageKey, profilesToShow[languageKey].length)} <TooltipsContainer> <tbody> - {this.renderProfiles(profilesToShow[languageKey])} + {profilesToShow[languageKey] != null && + this.renderProfiles(profilesToShow[languageKey])} </tbody> </TooltipsContainer> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListHeader.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListHeader.js index 3fd7c18d5ff..1d0c931aa04 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListHeader.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListHeader.js @@ -17,22 +17,26 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import { IndexLink } from 'react-router'; -import { LanguagesListType } from '../propTypes'; import { translate, translateWithParameters } from '../../../helpers/l10n'; +import { getProfilesPath, getProfilesForLanguagePath } from '../utils'; -export default class ProfilesListHeader extends React.Component { - static propTypes = { - languages: LanguagesListType.isRequired, - currentFilter: React.PropTypes.string - }; +type Props = { + currentFilter?: string, + languages: Array<{ key: string, name: string }>, + organization: ?string +}; + +export default class ProfilesListHeader extends React.PureComponent { + props: Props; renderFilterToggle() { const { languages, currentFilter } = this.props; const currentLanguage = currentFilter && languages.find(l => l.key === currentFilter); - const label = currentFilter + const label = currentLanguage ? translateWithParameters('quality_profiles.x_Profiles', currentLanguage.name) : translate('quality_profiles.all_profiles'); @@ -50,14 +54,14 @@ export default class ProfilesListHeader extends React.Component { return ( <ul className="dropdown-menu"> <li> - <IndexLink to="/profiles/"> + <IndexLink to={getProfilesPath(this.props.organization)}> {translate('quality_profiles.all_profiles')} </IndexLink> </li> {this.props.languages.map(language => ( <li key={language.key}> <IndexLink - to={{ pathname: '/profiles/', query: { language: language.key } }} + to={getProfilesForLanguagePath(language.key, this.props.organization)} className="js-language-filter-option" data-language={language.key}> {language.name} diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js index eca33432e52..3a53ee66e19 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/home/ProfilesListRow.js @@ -17,26 +17,30 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import React from 'react'; import { Link } from 'react-router'; import shallowCompare from 'react-addons-shallow-compare'; import ProfileLink from '../components/ProfileLink'; import ProfileDate from '../components/ProfileDate'; import ProfileActions from '../components/ProfileActions'; -import { ProfileType } from '../propTypes'; import { translate } from '../../../helpers/l10n'; import { getRulesUrl } from '../../../helpers/urls'; import { isStagnant } from '../utils'; +import type { Profile } from '../propTypes'; -export default class ProfilesListRow extends React.Component { - static propTypes = { - profile: ProfileType.isRequired, - canAdmin: React.PropTypes.bool.isRequired, - updateProfiles: React.PropTypes.func.isRequired - }; +type Props = { + canAdmin: boolean, + organization: ?string, + profile: Profile, + updateProfiles: () => Promise<*> +}; - shouldComponentUpdate(nextProps, nextState) { - return shallowCompare(this, nextProps, nextState); +export default class ProfilesListRow extends React.PureComponent { + props: Props; + + shouldComponentUpdate(nextProps: Props) { + return shallowCompare(this, nextProps); } renderName() { @@ -44,7 +48,7 @@ export default class ProfilesListRow extends React.Component { const offset = 25 * (profile.depth - 1); return ( <div style={{ paddingLeft: offset }}> - <ProfileLink profileKey={profile.key}> + <ProfileLink organization={this.props.organization} profileKey={profile.key}> {profile.name} </ProfileLink> </div> @@ -70,6 +74,8 @@ export default class ProfilesListRow extends React.Component { } renderRules() { + // FIXME getRulesUrl + const { profile } = this.props; const activeRulesUrl = getRulesUrl({ @@ -150,8 +156,9 @@ export default class ProfilesListRow extends React.Component { <i className="icon-dropdown" /> </button> <ProfileActions - profile={this.props.profile} canAdmin={this.props.canAdmin} + organization={this.props.organization} + profile={this.props.profile} updateProfiles={this.props.updateProfiles} /> </div> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/propTypes.js b/server/sonar-web/src/main/js/apps/quality-profiles/propTypes.js index 5ba29f3a37e..f8438011916 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/propTypes.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/propTypes.js @@ -17,10 +17,35 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import { PropTypes } from 'react'; const { shape, string, number, bool, arrayOf } = PropTypes; +export type Profile = { + key: string, + name: string, + isDefault: boolean, + isInherited: boolean, + language: string, + languageName: string, + activeRuleCount: number, + activeDeprecatedRuleCount: number, + projectCount?: number, + parentKey?: string, + parentName?: string, + userUpdatedAt?: string, + lastUsed?: string, + rulesUpdatedAt: string, + depth: number +}; + +export type Exporter = { + key: string, + name: string, + languages: Array<string> +}; + export const ProfileType = shape({ key: string.isRequired, name: string.isRequired, diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/routes.js b/server/sonar-web/src/main/js/apps/quality-profiles/routes.js index ac8f4c22894..57c5ca1baad 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/routes.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/routes.js @@ -19,9 +19,15 @@ */ const routes = [ { - getComponent(_, callback) { + getComponent(state, callback) { require.ensure([], require => { - callback(null, require('./components/AppContainer').default); + const AppContainer = require('./components/AppContainer').default; + if (state.params.organizationKey) { + callback(null, AppContainer); + } else { + const forSingleOrganization = require('../organizations/forSingleOrganization').default; + callback(null, forSingleOrganization(AppContainer)); + } }); }, getIndexRoute(_, callback) { diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-create-profile.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-create-profile.hbs index 845e4843ef6..acdc7f93805 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-create-profile.hbs +++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-create-profile.hbs @@ -1,14 +1,16 @@ -<form id="create-profile-form" action="{{link '/api/qualityprofiles/create'}}" enctype="multipart/form-data" - method="POST"> +<form id="create-profile-form" action="{{link '/api/qualityprofiles/create'}}" enctype="multipart/form-data" method="POST"> <div class="modal-head"> <h2>{{t 'quality_profiles.new_profile'}}</h2> </div> <div class="modal-body"> <div class="js-modal-messages"></div> + {{#if organization}} + <input type="hidden" name="organization" value="{{organization}}"> + {{/if}} <div class="modal-field"> - <label for="create-profile-name">{{t 'name'}}<em class="mandatory">*</em></label> - <input id="create-profile-name" name="name" type="text" size="50" maxlength="100" required> - </div> + <label for="create-profile-name">{{t 'name'}}<em class="mandatory">*</em></label> + <input id="create-profile-name" name="name" type="text" size="50" maxlength="100" required> + </div> <div class="modal-field spacer-bottom"> <label for="create-profile-language">{{t 'language'}}<em class="mandatory">*</em></label> <select id="create-profile-language" name="language" required> @@ -18,15 +20,15 @@ </select> </div> {{#each importers}} - <div class="modal-field spacer-bottom js-importer" data-key="{{key}}"> - <label for="create-profile-form-backup-{{key}}">{{name}}</label> - <input id="create-profile-form-backup-{{key}}" name="backup_{{key}}" type="file"> - <p class="note">{{t 'quality_profiles.optional_configuration_file'}}</p> - </div> + <div class="modal-field spacer-bottom js-importer" data-key="{{key}}"> + <label for="create-profile-form-backup-{{key}}">{{name}}</label> + <input id="create-profile-form-backup-{{key}}" name="backup_{{key}}" type="file"> + <p class="note">{{t 'quality_profiles.optional_configuration_file'}}</p> + </div> {{/each}} </div> <div class="modal-foot"> <button id="create-profile-submit">{{t 'create'}}</button> <a href="#" class="js-modal-close" id="create-profile-cancel">{{t 'cancel'}}</a> </div> -</form> +</form>
\ No newline at end of file diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-profile.hbs b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-profile.hbs index 9a8b0182717..7c22c83aab5 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-profile.hbs +++ b/server/sonar-web/src/main/js/apps/quality-profiles/templates/quality-profiles-restore-profile.hbs @@ -5,6 +5,9 @@ <div class="modal-body"> <div class="js-modal-messages"></div> + {{#if organization}} + <input type="hidden" name="organization" value="{{organization}}"> + {{/if}} {{#if profile}} {{#if ruleFailures}} <div class="alert alert-warning"> diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/utils.js b/server/sonar-web/src/main/js/apps/quality-profiles/utils.js index d5ea476bab8..64bb0ea07d6 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/utils.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/utils.js @@ -20,14 +20,9 @@ // @flow import { sortBy } from 'lodash'; import moment from 'moment'; +import type { Profile } from './propTypes'; -type Profiles = Array<{ - key: string, - name: string, - parentKey?: string -}>; - -export function sortProfiles(profiles: Profiles) { +export function sortProfiles(profiles: Array<Profile>) { const result = []; const sorted = sortBy(profiles, 'name'); @@ -67,6 +62,56 @@ export function createFakeProfile(overrides: {}) { }; } -export function isStagnant(profile: { userUpdatedAt: string }) { +export function isStagnant(profile: Profile) { return moment().diff(moment(profile.userUpdatedAt), 'years') >= 1; } + +export const getProfilesPath = (organization: ?string) => + organization ? `/organizations/${organization}/quality_profiles` : '/profiles'; + +export const getProfilesForLanguagePath = (language: string, organization: ?string) => ({ + pathname: organization ? `/organizations/${organization}/quality_profiles` : '/profiles', + query: { language } +}); + +export const getProfilePath = (profile: string, organization: ?string) => ({ + pathname: organization + ? `/organizations/${organization}/quality_profiles/show` + : '/profiles/show', + query: { key: profile } +}); + +export const getProfileComparePath = (profile: string, organization: ?string, withKey?: string) => { + const query: Object = { key: profile }; + if (withKey) { + Object.assign(query, { withKey }); + } + return { + pathname: organization + ? `/organizations/${organization}/quality_profiles/compare` + : '/profiles/compare', + query + }; +}; + +export const getProfileChangelogPath = ( + profile: string, + organization: ?string, + filter?: { since?: string, to?: string } +) => { + const query: Object = { key: profile }; + if (filter) { + if (filter.since) { + Object.assign(query, { since: filter.since }); + } + if (filter.to) { + Object.assign(query, { to: filter.to }); + } + } + return { + pathname: organization + ? `/organizations/${organization}/quality_profiles/changelog` + : '/profiles/changelog', + query + }; +}; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeParentView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeParentView.js index 01c83a69ee1..912aecb10cb 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeParentView.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeParentView.js @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import ModalFormView from '../../../components/common/modal-form'; import Template from '../templates/quality-profiles-change-profile-parent.hbs'; import { changeProfileParent } from '../../../api/quality-profiles'; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeProjectsView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeProjectsView.js index df5a7facd0c..c08cc80a8cf 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeProjectsView.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/views/ChangeProjectsView.js @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import escapeHtml from 'escape-html'; import ModalFormView from '../../../components/common/modal-form'; import Template from '../templates/quality-profiles-change-projects.hbs'; @@ -27,6 +28,8 @@ export default ModalFormView.extend({ template: Template, onRender() { + // TODO remove uuid usage + ModalFormView.prototype.onRender.apply(this, arguments); const { key } = this.options.profile; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/CopyProfileView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/CopyProfileView.js index c16397a652f..711946b8bfa 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/views/CopyProfileView.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/views/CopyProfileView.js @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import ModalFormView from '../../../components/common/modal-form'; import Template from '../templates/quality-profiles-copy-profile.hbs'; import { copyProfile } from '../../../api/quality-profiles'; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/CreateProfileView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/CreateProfileView.js index 8e12b9668a1..fb74811ec69 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/views/CreateProfileView.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/views/CreateProfileView.js @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import $ from 'jquery'; import ModalFormView from '../../../components/common/modal-form'; import Template from '../templates/quality-profiles-create-profile.hbs'; @@ -87,7 +88,8 @@ export default ModalFormView.extend({ return { ...ModalFormView.prototype.serializeData.apply(this, arguments), languages: this.options.languages, - importers: this.options.importers + importers: this.options.importers, + organization: this.options.organization }; } }); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/DeleteProfileView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/DeleteProfileView.js index 0c5d8a61732..ec4c5f878a4 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/views/DeleteProfileView.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/views/DeleteProfileView.js @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import ModalFormView from '../../../components/common/modal-form'; import Template from '../templates/quality-profiles-delete-profile.hbs'; import { deleteProfile } from '../../../api/quality-profiles'; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/RenameProfileView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/RenameProfileView.js index daf8db1ff04..308efa7c143 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/views/RenameProfileView.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/views/RenameProfileView.js @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import ModalFormView from '../../../components/common/modal-form'; import Template from '../templates/quality-profiles-rename-profile.hbs'; import { renameProfile } from '../../../api/quality-profiles'; diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreBuiltInProfilesView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreBuiltInProfilesView.js index 39d0c720887..2edf05044b4 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreBuiltInProfilesView.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreBuiltInProfilesView.js @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import ModalFormView from '../../../components/common/modal-form'; import Template from '../templates/quality-profiles-restore-built-in-profiles.hbs'; import TemplateSuccess from '../templates/quality-profiles-restore-built-in-profiles-success.hbs'; @@ -47,7 +48,10 @@ export default ModalFormView.extend({ sendRequest() { const language = this.$('#restore-built-in-profiles-language').val(); this.selectedLanguage = this.options.languages.find(l => l.key === language).name; - restoreBuiltInProfiles(language) + const data = this.options.organization + ? { language, organization: this.options.organization } + : { language }; + restoreBuiltInProfiles(data) .then(() => { this.done = true; this.render(); diff --git a/server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreProfileView.js b/server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreProfileView.js index ebd1fabe870..d2393e5053d 100644 --- a/server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreProfileView.js +++ b/server/sonar-web/src/main/js/apps/quality-profiles/views/RestoreProfileView.js @@ -17,6 +17,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +// @flow import ModalFormView from '../../../components/common/modal-form'; import Template from '../templates/quality-profiles-restore-profile.hbs'; import { restoreQualityProfile } from '../../../api/quality-profiles'; @@ -47,6 +48,7 @@ export default ModalFormView.extend({ serializeData() { return { ...ModalFormView.prototype.serializeData.apply(this, arguments), + organization: this.options.organization, profile: this.profile, ruleSuccesses: this.ruleSuccesses, ruleFailures: this.ruleFailures diff --git a/server/sonar-web/src/main/js/helpers/urls.js b/server/sonar-web/src/main/js/helpers/urls.js index 571d7a6069e..de25aa553ee 100644 --- a/server/sonar-web/src/main/js/helpers/urls.js +++ b/server/sonar-web/src/main/js/helpers/urls.js @@ -17,6 +17,8 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +import { getProfilePath } from '../apps/quality-profiles/utils'; + /** * Generate URL for a component's home page * @param {string} componentKey @@ -92,11 +94,8 @@ export function getComponentPermissionsUrl(componentKey) { * @param {string} key * @returns {Object} */ -export function getQualityProfileUrl(key) { - return { - pathname: '/profiles/show', - query: { key } - }; +export function getQualityProfileUrl(key, organization) { + return getProfilePath(key, organization); } /** |