import it.projectAdministration.BulkDeletionTest;
import it.projectAdministration.ProjectAdministrationTest;
import it.projectAdministration.ProjectLinksPageTest;
+import it.projectSearch.ProjectsPageTest;
import it.qualityGate.QualityGateNotificationTest;
import it.qualityGate.QualityGateTest;
import it.qualityGate.QualityGateUiTest;
// measure
ProjectMeasuresPageTest.class,
ProjectDashboardTest.class,
+ ProjectsPageTest.class,
MeasuresWsTest.class,
// measure history
DifferentialPeriodsTest.class,
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package it.projectSearch;
+
+import com.sonar.orchestrator.Orchestrator;
+import com.sonar.orchestrator.build.SonarScanner;
+import it.Category1Suite;
+import org.junit.BeforeClass;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import pageobjects.Navigation;
+import pageobjects.projects.ProjectsPage;
+
+import static util.ItUtils.projectDir;
+
+public class ProjectsPageTest {
+
+ @ClassRule
+ public static Orchestrator ORCHESTRATOR = Category1Suite.ORCHESTRATOR;
+
+ @Rule
+ public Navigation nav = Navigation.get(ORCHESTRATOR);
+
+ @BeforeClass
+ public static void setUp() {
+ ORCHESTRATOR.resetData();
+ ORCHESTRATOR.executeBuild(SonarScanner.create(projectDir("shared/xoo-sample")).setProjectKey("key-foo"));
+ ORCHESTRATOR.executeBuild(SonarScanner.create(projectDir("duplications/file-duplications")).setProjectKey("key-bar"));
+ }
+
+ @Test
+ public void should_display_projects() {
+ ProjectsPage page = nav.openProjects();
+ page.shouldHaveTotal(2);
+ page.getProjectByKey("key-foo")
+ .shouldHaveMeasure("reliability_rating", "A")
+ .shouldHaveMeasure("security_rating", "A")
+ .shouldHaveMeasure("sqale_rating", "A")
+ .shouldHaveMeasure("duplicated_lines_density", "0.0%")
+ .shouldHaveMeasure("ncloc", "13")
+ .shouldHaveMeasure("ncloc", "Xoo");
+ }
+
+ @Test
+ public void should_display_facets() {
+ ProjectsPage page = nav.openProjects();
+ page.getFacetByProperty("duplications")
+ .shouldHaveValue("1", "1")
+ .shouldHaveValue("2", "1")
+ .shouldHaveValue("3", "1")
+ .shouldHaveValue("4", "1")
+ .shouldHaveValue("5", "1");
+ }
+
+ @Test
+ public void should_filter_using_facet() {
+ ProjectsPage page = nav.openProjects();
+ page.shouldHaveTotal(2);
+ page.getFacetByProperty("duplications").selectValue("3");
+ page.shouldHaveTotal(1);
+ }
+
+}
import org.junit.rules.ExternalResource;
import org.openqa.selenium.By;
import pageobjects.licenses.LicensesPage;
+import pageobjects.projects.ProjectsPage;
import pageobjects.settings.SettingsPage;
import static com.codeborne.selenide.Selenide.$;
return open("/", Navigation.class);
}
+ public ProjectsPage openProjects() {
+ return open("/projects", ProjectsPage.class);
+ }
+
public ProjectDashboardPage openProjectDashboard(String projectKey) {
// TODO encode projectKey
String url = "/dashboard?id=" + projectKey;
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package pageobjects.projects;
+
+import com.codeborne.selenide.Condition;
+import com.codeborne.selenide.SelenideElement;
+
+public class FacetItem {
+
+ private final SelenideElement elt;
+
+ public FacetItem(SelenideElement elt) {
+ this.elt = elt;
+ }
+
+ public FacetItem shouldHaveValue(String key, String value) {
+ this.elt.$(".facet[data-key=\"" + key + "\"] .facet-stat").shouldHave(Condition.text(value));
+ return this;
+ }
+
+ public void selectValue(String key) {
+ this.elt.$(".facet[data-key=\"" + key + "\"]").click();
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package pageobjects.projects;
+
+import com.codeborne.selenide.Condition;
+import com.codeborne.selenide.SelenideElement;
+
+public class ProjectItem {
+
+ private final SelenideElement elt;
+
+ public ProjectItem(SelenideElement elt) {
+ this.elt = elt;
+ }
+
+ public ProjectItem shouldHaveMeasure(String metricKey, String value) {
+ this.elt.$(".project-card-measure[data-key=\"" + metricKey + "\"]").shouldHave(Condition.text(value));
+ return this;
+ }
+}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2016 SonarSource SA
+ * mailto:contact AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package pageobjects.projects;
+
+import com.codeborne.selenide.Condition;
+import com.codeborne.selenide.ElementsCollection;
+import com.codeborne.selenide.SelenideElement;
+
+import static com.codeborne.selenide.Condition.text;
+import static com.codeborne.selenide.Condition.visible;
+import static com.codeborne.selenide.Selenide.$;
+import static com.codeborne.selenide.Selenide.$$;
+
+public class ProjectsPage {
+
+ public ProjectsPage() {
+ $("#projects-page").shouldBe(visible);
+ }
+
+ public ElementsCollection getProjects() {
+ return $$(".projects-list > .boxed-group");
+ }
+
+ public ElementsCollection getFacets() {
+ return $$(".search-navigator-facet-box");
+ }
+
+ public ProjectItem getProjectByKey(String projectKey) {
+ SelenideElement element = getProjects().find(Condition.attribute("data-key", projectKey));
+ return new ProjectItem(element);
+ }
+
+ public FacetItem getFacetByProperty(String facetProperty) {
+ SelenideElement element = getFacets().find(Condition.attribute("data-key", facetProperty));
+ return new FacetItem(element);
+ }
+
+ public ProjectsPage shouldHaveTotal(int total) {
+ // warning - number is localized
+ $("#projects-total").shouldHave(text(String.valueOf(total)));
+ return this;
+ }
+}
{this.props.total != null && (
<span>
- <strong>{this.props.total}</strong> {translate('projects._projects')}
+ <strong id="projects-total">{this.props.total}</strong> {translate('projects._projects')}
</span>
)}
</div>
const className = classNames('boxed-group', 'project-card', { 'boxed-group-loading': this.props.measures == null });
return (
- <div className={className}>
+ <div data-key={project.key} className={className}>
{this.props.measures != null && (
<div className="boxed-group-actions">
<ProjectCardQualityGate status={this.props.measures['alert_status']}/>
return (
<div className="project-card-measures">
- <div className="project-card-measure">
+ <div className="project-card-measure" data-key="reliability_rating">
<div className="project-card-measure-inner">
<div className="project-card-measure-number">
<Rating value={measures['reliability_rating']}/>
</div>
</div>
- <div className="project-card-measure">
+ <div className="project-card-measure" data-key="security_rating">
<div className="project-card-measure-inner">
<div className="project-card-measure-number">
<Rating value={measures['security_rating']}/>
</div>
</div>
- <div className="project-card-measure">
+ <div className="project-card-measure" data-key="sqale_rating">
<div className="project-card-measure-inner">
<div className="project-card-measure-number">
<Rating value={measures['sqale_rating']}/>
</div>
</div>
- <div className="project-card-measure">
+ <div className="project-card-measure" data-key="coverage">
<div className="project-card-measure-inner">
<div className="project-card-measure-number">
{measures['coverage'] != null && (
</div>
</div>
- <div className="project-card-measure">
+ <div className="project-card-measure" data-key="duplicated_lines_density">
<div className="project-card-measure-inner">
<div className="project-card-measure-number">
<span className="spacer-right">
</div>
</div>
- <div className="project-card-measure">
+ <div className="project-card-measure" data-key="ncloc">
<div className="project-card-measure-inner">
<div className="project-card-measure-number">
<span className="spacer-right">
const facetValue = (facet && getFacetValueForOption) ? getFacetValueForOption(facet, option) : null;
return (
- <Link key={option} className={className} to={path}>
+ <Link key={option} className={className} to={path} data-key={option}>
<span className="facet-name">
{this.props.renderOption(option, option === value)}
</span>
render () {
return (
- <div className="search-navigator-facet-box">
+ <div className="search-navigator-facet-box" data-key={this.props.property}>
{this.renderHeader()}
{this.renderOptions()}
</div>