@@ -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 { | |||
@@ -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()); | |||
} | |||
} |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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); |
@@ -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()} |
@@ -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> |
@@ -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 |
@@ -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 = [ | |||
{ | |||
@@ -54,6 +55,10 @@ const routes = [ | |||
path: 'members', | |||
component: OrganizationMembersContainer | |||
}, | |||
{ | |||
path: 'quality_profiles', | |||
childRoutes: qualityProfilesRoutes | |||
}, | |||
{ | |||
component: OrganizationAdmin, | |||
childRoutes: [ |
@@ -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} /> | |||
@@ -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> |
@@ -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); |
@@ -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)); |
@@ -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; |
@@ -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 ( |
@@ -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"> |
@@ -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(); |
@@ -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; |
@@ -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; |
@@ -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 ( |
@@ -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; |
@@ -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"> |
@@ -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); | |||
} | |||
@@ -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; | |||
} |
@@ -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 | |||
}); | |||
} |
@@ -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); |
@@ -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>} |
@@ -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} |
@@ -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() { |
@@ -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} |
@@ -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> |
@@ -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 ( |
@@ -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() { |
@@ -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> |
@@ -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> |
@@ -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} |
@@ -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(); | |||
} | |||
@@ -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; | |||
} | |||
@@ -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; |
@@ -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> | |||
); |
@@ -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> |
@@ -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; | |||
} |
@@ -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> |
@@ -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> |
@@ -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> |
@@ -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> | |||
@@ -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} |
@@ -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> |
@@ -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, |
@@ -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) { |
@@ -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> |
@@ -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"> |
@@ -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 | |||
}; | |||
}; |
@@ -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'; |
@@ -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; |
@@ -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'; |
@@ -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 | |||
}; | |||
} | |||
}); |
@@ -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'; |
@@ -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'; |
@@ -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(); |
@@ -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 |
@@ -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); | |||
} | |||
/** |