import React from 'react';
import { Link } from 'react-router';
import { translate } from '../../../../helpers/l10n';
+import { getQualityGatesUrl } from '../../../../helpers/urls';
import { isMySet } from '../../../../apps/issues/utils';
export default class GlobalNavMenu extends React.PureComponent {
renderQualityGatesLink() {
return (
<li>
- <Link to="/quality_gates" activeClassName="active">
+ <Link to={getQualityGatesUrl()} activeClassName="active">
{translate('quality_gates.page')}
</Link>
</li>
{this.renderIssuesLink()}
{!organizationsEnabled && this.renderRulesLink()}
{!organizationsEnabled && this.renderProfilesLink()}
- {this.renderQualityGatesLink()}
+ {!organizationsEnabled && this.renderQualityGatesLink()}
{this.renderAdministrationLink()}
{this.renderMore()}
</ul>
activeClassName="active"
onlyActiveOnIndex={false}
style={Object {}}
- to="/quality_gates"
+ to={
+ Object {
+ "pathname": "/quality_gates",
+ }
+ }
>
quality_gates.page
</Link>
activeClassName="active"
onlyActiveOnIndex={false}
style={Object {}}
- to="/quality_gates"
+ to={
+ Object {
+ "pathname": "/quality_gates",
+ }
+ }
>
quality_gates.page
</Link>
import NavBarTabs from '../../../components/nav/NavBarTabs';
import OrganizationIcon from '../../../components/icons-components/OrganizationIcon';
import { isMySet } from '../../issues/utils';
+import { getQualityGatesUrl } from '../../../helpers/urls';
import type { Organization } from '../../../store/organizations/duck';
const ADMIN_PATHS = [
{translate('coding_rules.page')}
</Link>
</li>
+ <li>
+ <Link to={getQualityGatesUrl(organization.key)} activeClassName="active">
+ {translate('quality_gates.page')}
+ </Link>
+ </li>
{this.renderExtensions(moreActive)}
{organization.canAdmin && this.renderAdministration(adminActive)}
</NavBarTabs>
coding_rules.page
</Link>
</li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/organizations/foo/quality_gates",
+ }
+ }
+ >
+ quality_gates.page
+ </Link>
+ </li>
<li
className="dropdown"
>
coding_rules.page
</Link>
</li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/organizations/foo/quality_gates",
+ }
+ }
+ >
+ quality_gates.page
+ </Link>
+ </li>
</NavBarTabs>
</ContextNavBar>
`;
coding_rules.page
</Link>
</li>
+ <li>
+ <Link
+ activeClassName="active"
+ onlyActiveOnIndex={false}
+ style={Object {}}
+ to={
+ Object {
+ "pathname": "/organizations/foo/quality_gates",
+ }
+ }
+ >
+ quality_gates.page
+ </Link>
+ </li>
<li
className="dropdown"
>
import OrganizationPermissionTemplates from './components/OrganizationPermissionTemplates';
import OrganizationProjectsManagement from './components/OrganizationProjectsManagement';
import OrganizationDelete from './components/OrganizationDelete';
+import qualityGatesRoutes from '../quality-gates/routes';
import qualityProfilesRoutes from '../quality-profiles/routes';
import issuesRoutes from '../issues/routes';
path: 'quality_profiles',
childRoutes: qualityProfilesRoutes
},
+ {
+ path: 'quality_gates',
+ component: OrganizationContainer,
+ childRoutes: qualityGatesRoutes
+ },
{
path: 'extension/:pluginKey/:extensionKey',
component: OrganizationPageExtension
const shouldShowQualityProfiles = !isView && !isDeveloper && hasQualityProfiles;
const shouldShowQualityGate = !isView && !isDeveloper && hasQualityGate;
- const shouldShowOrganizationKey = component.organization != null && areThereCustomOrganizations;
+ const hasOrganization = component.organization != null && areThereCustomOrganizations;
return (
<div className="overview-meta">
{isProject && <AnalysesList project={component.key} history={history} router={router} />}
- {shouldShowQualityGate && <MetaQualityGate gate={qualityGate} />}
+ {shouldShowQualityGate &&
+ <MetaQualityGate
+ gate={qualityGate}
+ organization={hasOrganization && component.organization}
+ />}
{shouldShowQualityProfiles &&
<MetaQualityProfiles
<MetaKey component={component} />
- {shouldShowOrganizationKey && <MetaOrganizationKey component={component} />}
+ {hasOrganization && <MetaOrganizationKey component={component} />}
</div>
);
};
import { translate } from '../../../helpers/l10n';
import { getQualityGateUrl } from '../../../helpers/urls';
-const MetaQualityGate = ({ gate }) => {
+const MetaQualityGate = ({ gate, organization }) => {
return (
<div className="overview-meta-card">
<h4 className="overview-meta-header">
<span className="note spacer-right">
{'(' + translate('default') + ')'}
</span>}
- <Link to={getQualityGateUrl(gate.key)}>
+ <Link to={getQualityGateUrl(gate.key, organization)}>
{gate.name}
</Link>
</li>
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import React, { Component } from 'react';
+import React from 'react';
import Helmet from 'react-helmet';
import {
fetchQualityGate,
import RenameView from '../views/rename-view';
import CopyView from '../views/copy-view';
import DeleteView from '../views/delete-view';
+import { getQualityGatesUrl, getQualityGateUrl } from '../../../helpers/urls';
-export default class Details extends Component {
+export default class Details extends React.PureComponent {
componentDidMount() {
this.fetchDetails();
}
}
handleCopyClick() {
- const { qualityGate, onCopy } = this.props;
+ const { qualityGate, onCopy, organization } = this.props;
const { router } = this.context;
new CopyView({
qualityGate,
onCopy: newQualityGate => {
onCopy(newQualityGate);
- router.push(`/quality_gates/show/${newQualityGate.id}`);
+ router.push(getQualityGateUrl(newQualityGate.id, organization && organization.key));
}
}).render();
}
}
handleDeleteClick() {
- const { qualityGate, onDelete } = this.props;
+ const { qualityGate, onDelete, organization } = this.props;
const { router } = this.context;
-
new DeleteView({
qualityGate,
onDelete: qualityGate => {
onDelete(qualityGate);
- router.replace('/quality_gates');
+ router.replace(getQualityGatesUrl(organization && organization.key));
}
}).render();
}
onCopy={this.handleCopyClick.bind(this)}
onSetAsDefault={this.handleSetAsDefaultClick.bind(this)}
onDelete={this.handleDeleteClick.bind(this)}
+ organization={this.props.organization}
/>
<DetailsContent
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import React, { Component } from 'react';
+import React from 'react';
import Conditions from './Conditions';
import Projects from './Projects';
import { translate } from '../../../helpers/l10n';
-export default class DetailsContent extends Component {
+export default class DetailsContent extends React.PureComponent {
render() {
const { gate, canEdit, metrics } = this.props;
const { onAddCondition, onDeleteCondition, onSaveCondition } = this.props;
import React from 'react';
import { translate } from '../../../helpers/l10n';
-export default function DetailsHeader({
- qualityGate,
- edit,
- onRename,
- onCopy,
- onSetAsDefault,
- onDelete
-}) {
- function handleRenameClick(e) {
+export default class DetailsHeader extends React.PureComponent {
+ handleRenameClick = e => {
e.preventDefault();
- onRename();
- }
+ this.props.onRename();
+ };
- function handleCopyClick(e) {
+ handleCopyClick = e => {
e.preventDefault();
- onCopy();
- }
+ this.props.onCopy();
+ };
- function handleSetAsDefaultClick(e) {
+ handleSetAsDefaultClick = e => {
e.preventDefault();
- onSetAsDefault();
- }
+ this.props.onSetAsDefault();
+ };
- function handleDeleteClick(e) {
+ handleDeleteClick = e => {
e.preventDefault();
- onDelete();
- }
+ this.props.onDelete();
+ };
+
+ render() {
+ const { qualityGate, edit } = this.props;
- return (
- <div className="layout-page-header-panel layout-page-main-header issues-main-header">
- <div className="layout-page-header-panel-inner layout-page-main-header-inner">
- <div className="layout-page-main-inner">
- <h2 className="pull-left">
- {qualityGate.name}
- </h2>
- {edit &&
- <div className="pull-right">
- <div className="button-group">
- <button id="quality-gate-rename" onClick={handleRenameClick}>
- {translate('rename')}
- </button>
- <button id="quality-gate-copy" onClick={handleCopyClick}>
- {translate('copy')}
- </button>
- <button id="quality-gate-toggle-default" onClick={handleSetAsDefaultClick}>
- {qualityGate.isDefault
- ? translate('unset_as_default')
- : translate('set_as_default')}
- </button>
- <button id="quality-gate-delete" className="button-red" onClick={handleDeleteClick}>
- {translate('delete')}
- </button>
- </div>
- </div>}
+ return (
+ <div className="layout-page-header-panel layout-page-main-header issues-main-header">
+ <div className="layout-page-header-panel-inner layout-page-main-header-inner">
+ <div className="layout-page-main-inner">
+ <h2 className="pull-left">
+ {qualityGate.name}
+ </h2>
+ {edit &&
+ <div className="pull-right">
+ <div className="button-group">
+ <button id="quality-gate-rename" onClick={this.handleRenameClick}>
+ {translate('rename')}
+ </button>
+ <button id="quality-gate-copy" onClick={this.handleCopyClick}>
+ {translate('copy')}
+ </button>
+ <button id="quality-gate-toggle-default" onClick={this.handleSetAsDefaultClick}>
+ {qualityGate.isDefault
+ ? translate('unset_as_default')
+ : translate('set_as_default')}
+ </button>
+ <button
+ id="quality-gate-delete"
+ className="button-red"
+ onClick={this.handleDeleteClick}>
+ {translate('delete')}
+ </button>
+ </div>
+ </div>}
+ </div>
</div>
</div>
- </div>
- );
+ );
+ }
}
import React from 'react';
import { Link } from 'react-router';
import { translate } from '../../../helpers/l10n';
+import { getQualityGateUrl } from '../../../helpers/urls';
-export default function List({ qualityGates }) {
+export default function List({ organization, qualityGates }) {
return (
<div className="list-group">
{qualityGates.map(qualityGate =>
<Link
key={qualityGate.id}
- to={`/quality_gates/show/${qualityGate.id}`}
+ to={getQualityGateUrl(qualityGate.id, organization && organization.key)}
activeClassName="active"
className="list-group-item"
data-id={qualityGate.id}>
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import React, { Component } from 'react';
+import React from 'react';
import ProjectsView from '../views/gate-projects-view';
-export default class Projects extends Component {
+export default class Projects extends React.PureComponent {
componentDidMount() {
this.renderView();
}
fetchQualityGates as fetchQualityGatesAPI
} from '../../../api/quality-gates';
import { translate } from '../../../helpers/l10n';
+import { getQualityGateUrl } from '../../../helpers/urls';
import '../styles.css';
export default class QualityGatesApp extends Component {
+ static contextTypes = {
+ router: React.PropTypes.object.isRequired
+ };
+
state = {};
componentDidMount() {
}
handleAdd(qualityGate) {
- const { addQualityGate } = this.props;
+ const { addQualityGate, organization } = this.props;
const { router } = this.context;
addQualityGate(qualityGate);
- router.push(`/quality_gates/show/${qualityGate.id}`);
+ router.push(getQualityGateUrl(qualityGate.id, organization && organization.key));
}
render() {
- const { children, qualityGates, edit } = this.props;
+ const { children, qualityGates, edit, organization } = this.props;
const defaultTitle = translate('quality_gates.page');
+ const top = organization ? 95 : 30;
return (
<div className="layout-page">
<Helmet defaultTitle={defaultTitle} titleTemplate={'%s - ' + defaultTitle} />
<div className="layout-page-side-outer">
- <div className="layout-page-side" style={{ top: 30 }}>
+ <div className="layout-page-side" style={{ top }}>
<div className="layout-page-side-inner">
<div className="layout-page-filters">
<ListHeader canEdit={edit} onAdd={this.handleAdd.bind(this)} />
- {qualityGates && <List qualityGates={qualityGates} />}
+ {qualityGates && <List organization={organization} qualityGates={qualityGates} />}
</div>
</div>
</div>
</div>
- {!!qualityGates && children}
+ {qualityGates != null &&
+ React.Children.map(children, child => React.cloneElement(child, { organization }))}
</div>
);
}
}
-
-QualityGatesApp.contextTypes = {
- router: React.PropTypes.object.isRequired
-};
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
-import { getComponentUrl, getComponentIssuesUrl, getComponentDrilldownUrl } from '../urls';
+import {
+ getComponentUrl,
+ getComponentIssuesUrl,
+ getComponentDrilldownUrl,
+ getQualityGatesUrl,
+ getQualityGateUrl
+} from '../urls';
const SIMPLE_COMPONENT_KEY = 'sonarqube';
const COMPLEX_COMPONENT_KEY = 'org.sonarsource.sonarqube:sonarqube';
});
});
- it('should encode component key', () => {
+ it('should not encode component key', () => {
expect(getComponentDrilldownUrl(COMPLEX_COMPONENT_KEY, METRIC)).toEqual({
pathname: '/component_measures/metric/' + METRIC,
query: { id: COMPLEX_COMPONENT_KEY }
});
});
});
+
+describe('#getQualityGate(s)Url', () => {
+ it('should take organization key into account', () => {
+ expect(getQualityGatesUrl()).toEqual({ pathname: '/quality_gates' });
+ expect(getQualityGatesUrl('foo')).toEqual({ pathname: '/organizations/foo/quality_gates' });
+ expect(getQualityGateUrl('bar')).toEqual({ pathname: '/quality_gates/show/bar' });
+ expect(getQualityGateUrl('bar', 'foo')).toEqual({
+ pathname: '/organizations/foo/quality_gates/show/bar'
+ });
+ });
+
+ it('should encode keys', () => {
+ expect(getQualityGatesUrl(COMPLEX_COMPONENT_KEY)).toEqual({
+ pathname: '/organizations/' + COMPLEX_COMPONENT_KEY_ENCODED + '/quality_gates'
+ });
+ expect(getQualityGateUrl(COMPLEX_COMPONENT_KEY)).toEqual({
+ pathname: '/quality_gates/show/' + COMPLEX_COMPONENT_KEY_ENCODED
+ });
+ });
+});
return getProfilePath(name, language, organization);
}
-/**
- * Generate URL for a quality gate
- * @param {string} key
- * @returns {Object}
- */
-export function getQualityGateUrl(key) {
- return {
- pathname: '/quality_gates/show/' + encodeURIComponent(key)
- };
-}
+export const getQualityGateUrl = (key: string, organization?: string) => ({
+ pathname: getQualityGatesUrl(organization).pathname + '/show/' + encodeURIComponent(key)
+});
+
+export const getQualityGatesUrl = (organization?: string) => ({
+ pathname:
+ (organization ? '/organizations/' + encodeURIComponent(organization) : '') + '/quality_gates'
+});
/**
* Generate URL for the rules page
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.html5.WebStorage;
-import org.sonarqube.pageobjects.issues.Issue;
import org.sonarqube.tests.Tester;
import org.sonarqube.pageobjects.issues.IssuesPage;
import org.sonarqube.pageobjects.licenses.LicensesPage;
return open(url, ProjectLinksPage.class);
}
+ public QualityGatePage openQualityGates() {
+ String url = "/quality_gates";
+ return open(url, QualityGatePage.class);
+ }
+
+ public QualityGatePage openQualityGates(String organization) {
+ String url = "/organizations/" + organization + "/quality_gates";
+ return open(url, QualityGatePage.class);
+ }
+
public ProjectQualityGatePage openProjectQualityGate(String projectKey) {
// TODO encode projectKey
String url = "/project/quality_gate?id=" + projectKey;
import com.codeborne.selenide.ElementsCollection;
import com.codeborne.selenide.SelenideElement;
import java.util.Arrays;
+import org.openqa.selenium.By;
import static com.codeborne.selenide.Condition.exist;
import static com.codeborne.selenide.Condition.hasText;
import static com.codeborne.selenide.Condition.visible;
import static com.codeborne.selenide.Selenide.$;
import static com.codeborne.selenide.Selenide.$$;
+import static org.assertj.core.api.Assertions.assertThat;
public class ProjectDashboardPage {
tagsInput.sendKeys(charSequences);
return this;
}
+
+ public ProjectDashboardPage hasQualityGateLink(String name, String link) {
+ SelenideElement elem = $(".overview-meta-header").should(exist)
+ .parent().find(By.linkText(name)).should(exist);
+ assertThat(elem.attr("href")).endsWith(link);
+ return this;
+ }
}
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonarqube.pageobjects;
+
+import com.codeborne.selenide.Condition;
+
+import static com.codeborne.selenide.Selenide.$;
+import static com.codeborne.selenide.Selenide.$$;
+
+public class QualityGatePage {
+ public QualityGatePage() {
+ $(".quality-gates-results").shouldBe(Condition.visible);
+ }
+
+ public QualityGatePage countQualityGates(Integer count) {
+ $$(".quality-gates-results .list-group-item").shouldHaveSize(count);
+ return this;
+ }
+
+ public QualityGatePage canCreateQG() {
+ $("#quality-gate-add").should(Condition.exist).shouldBe(Condition.visible);
+ return this;
+ }
+
+ public QualityGatePage canNotCreateQG() {
+ $("#quality-gate-add").shouldNot(Condition.exist);
+ return this;
+ }
+}
import org.sonarqube.tests.projectAdministration.ProjectProvisioningTest;
import org.sonarqube.tests.projectSearch.LeakProjectsPageTest;
import org.sonarqube.tests.projectSearch.SearchProjectsTest;
+import org.sonarqube.tests.qualityGate.OrganizationQualityGateUiTest;
import org.sonarqube.tests.qualityProfile.BuiltInQualityProfilesTest;
import org.sonarqube.tests.qualityProfile.CustomQualityProfilesTest;
import org.sonarqube.tests.qualityProfile.OrganizationQualityProfilesUiTest;
OrganizationIssuesPageTest.class,
OrganizationMembershipTest.class,
OrganizationMembershipUiTest.class,
+ OrganizationQualityGateUiTest.class,
OrganizationQualityProfilesUiTest.class,
OrganizationTest.class,
RootUserOnOrganizationTest.class,
--- /dev/null
+/*
+ * SonarQube
+ * Copyright (C) 2009-2017 SonarSource SA
+ * mailto:info AT sonarsource DOT com
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+package org.sonarqube.tests.qualityGate;
+
+import com.codeborne.selenide.Condition;
+import com.codeborne.selenide.SelenideElement;
+import com.sonar.orchestrator.Orchestrator;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.openqa.selenium.By;
+import org.sonarqube.pageobjects.ProjectDashboardPage;
+import org.sonarqube.pageobjects.QualityGatePage;
+import org.sonarqube.tests.Category6Suite;
+import org.sonarqube.tests.Tester;
+import org.sonarqube.ws.Organizations;
+import org.sonarqube.ws.WsUsers;
+import util.issue.IssueRule;
+
+import static com.codeborne.selenide.Selenide.$;
+import static util.ItUtils.restoreProfile;
+import static util.ItUtils.runProjectAnalysis;
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class OrganizationQualityGateUiTest {
+ @ClassRule
+ public static Orchestrator orchestrator = Category6Suite.ORCHESTRATOR;
+
+ @Rule
+ public Tester tester = new Tester(orchestrator);
+
+ @Rule
+ public IssueRule issueRule = IssueRule.from(orchestrator);
+
+ private Organizations.Organization organization;
+ private WsUsers.CreateWsResponse.User user;
+
+ @Before
+ public void setUp() throws Exception {
+ organization = tester.organizations().generate();
+ user = tester.users().generate();
+ tester.organizations().addMember(organization, user);
+ restoreProfile(orchestrator, getClass().getResource("/issue/with-many-rules.xml"), organization.getKey());
+ }
+
+ @Test
+ public void should_have_a_link_to_quality_gates() {
+ tester.openBrowser()
+ .logIn().submitCredentials(user.getLogin())
+ .openQualityGates(organization.getKey());
+
+ SelenideElement element = $(".navbar-context .navbar-nav")
+ .find(By.linkText("Quality Gates"))
+ .should(Condition.exist);
+ assertThat(element.attr("href")).endsWith("/organizations/" + organization.getKey() + "/quality_gates");
+ }
+
+ @Test
+ public void should_display_available_quality_gates() {
+ QualityGatePage page = tester.openBrowser()
+ .logIn().submitCredentials(user.getLogin())
+ .openQualityGates(organization.getKey());
+ page.countQualityGates(1);
+ }
+
+ @Test
+ public void should_not_allow_random_user_to_create() {
+ tester.openBrowser()
+ .logIn().submitCredentials(user.getLogin())
+ .openQualityGates(organization.getKey())
+ .canNotCreateQG();
+ tester.openBrowser()
+ .logIn().submitCredentials("admin")
+ .openQualityGates(organization.getKey())
+ .canCreateQG();
+ }
+
+ @Test
+ public void quality_gate_link_on_project_dashboard_should_have_organization_context() {
+ String project = tester.projects().generate(organization).getKey();
+ runProjectAnalysis(orchestrator, "shared/xoo-multi-modules-sample",
+ "sonar.projectKey", project,
+ "sonar.organization", organization.getKey(),
+ "sonar.login", "admin",
+ "sonar.password", "admin",
+ "sonar.scm.disabled", "false",
+ "sonar.scm.provider", "xoo");
+
+ String link = "/organizations/" + organization.getKey() + "/quality_gates/show/1";
+ ProjectDashboardPage page = tester.openBrowser()
+ .logIn().submitCredentials(user.getLogin())
+ .openProjectDashboard(project);
+ page.hasQualityGateLink("SonarQube way", link);
+ }
+}
*/
package org.sonarqube.tests.qualityGate;
+import com.codeborne.selenide.Condition;
+import com.codeborne.selenide.SelenideElement;
import com.sonar.orchestrator.Orchestrator;
import com.sonar.orchestrator.build.SonarScanner;
+import org.junit.Rule;
+import org.openqa.selenium.By;
import org.sonarqube.tests.Category1Suite;
import java.util.Date;
import javax.annotation.Nullable;
import org.sonar.wsclient.qualitygate.UpdateCondition;
import org.sonarqube.pageobjects.Navigation;
import org.sonarqube.pageobjects.ProjectActivityPage;
+import org.sonarqube.tests.Tester;
+import static com.codeborne.selenide.Selenide.$;
import static org.apache.commons.lang.time.DateUtils.addDays;
+import static org.assertj.core.api.Assertions.assertThat;
import static util.ItUtils.projectDir;
import static util.ItUtils.resetPeriod;
import static util.ItUtils.setServerProperty;
@ClassRule
public static Orchestrator orchestrator = Category1Suite.ORCHESTRATOR;
+ @Rule
+ public Tester tester = new Tester(orchestrator);
+
private static long DEFAULT_QUALITY_GATE;
@BeforeClass
runSelenese(orchestrator, "/qualityGate/QualityGateUiTest/should_display_quality_gates_page.html");
}
+ @Test
+ public void should_have_a_global_link_to_quality_gates() {
+ String login = tester.users().generate().getLogin();
+ tester.openBrowser()
+ .logIn().submitCredentials(login)
+ .openQualityGates();
+
+ SelenideElement element = $(".navbar-global .navbar-nav")
+ .find(By.linkText("Quality Gates"))
+ .should(Condition.exist);
+ assertThat(element.attr("href")).endsWith("/quality_gates");
+ }
+
+ @Test
+ public void should_not_allow_random_user_to_create() {
+ String login = tester.users().generate().getLogin();
+ tester.openBrowser()
+ .logIn().submitCredentials(login)
+ .openQualityGates()
+ .canNotCreateQG();
+ tester.openBrowser()
+ .logIn().submitCredentials("admin")
+ .openQualityGates()
+ .canCreateQG();
+ }
+
private void scanSampleWithDate(String date) {
scanSample(date, null);
}