import com.sonar.orchestrator.build.SonarScanner;
import com.sonar.orchestrator.selenium.Selenese;
import it.Category1Suite;
+import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Test;
+import pageobjects.BackgroundTaskItem;
+import pageobjects.BackgroundTasksPage;
+import pageobjects.Navigation;
import util.selenium.SeleneseTest;
import static util.ItUtils.projectDir;
@ClassRule
public static Orchestrator orchestrator = Category1Suite.ORCHESTRATOR;
- @Test
- public void should_not_display_failing_and_search_and_filter_elements_on_project_level_page() throws Exception {
+ @BeforeClass
+ public static void beforeClass() {
executeBuild("test-project", "Test Project");
executeBuild("test-project-2", "Another Test Project");
+ }
+ @Test
+ public void should_not_display_failing_and_search_and_filter_elements_on_project_level_page() throws Exception {
Selenese selenese = Selenese.builder().setHtmlTestsInClasspath("should_not_display_failing_and_search_and_filter_elements_on_project_level_page",
"/projectAdministration/BackgroundTasksTest/should_not_display_failing_and_search_and_filter_elements_on_project_level_page.html"
).build();
new SeleneseTest(selenese).runOn(orchestrator);
}
- private void executeBuild(String projectKey, String projectName) {
+ @Test
+ public void display_scanner_context() {
+ Navigation nav = Navigation.get(orchestrator);
+ nav.openHomepage().logIn().submitCredentials("admin", "admin");
+ BackgroundTasksPage page = nav.openBackgroundTasksPage();
+
+ BackgroundTaskItem task = page.getTasksAsItems().get(0);
+ task.openActions()
+ .openScannerContext()
+ .assertScannerContextContains("SonarQube plugins:")
+ .assertScannerContextContains("Global properties:");
+ }
+
+ private static void executeBuild(String projectKey, String projectName) {
orchestrator.executeBuild(
SonarScanner.create(projectDir("shared/xoo-sample"))
.setProjectKey(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;
+
+import com.codeborne.selenide.SelenideElement;
+
+import static com.codeborne.selenide.Condition.hasText;
+import static com.codeborne.selenide.Condition.visible;
+import static com.codeborne.selenide.Selenide.$;
+
+public class BackgroundTaskItem {
+
+ private final SelenideElement elt;
+
+ public BackgroundTaskItem(SelenideElement elt) {
+ this.elt = elt;
+ }
+
+ public SelenideElement getComponent() {
+ return elt.$("td:nth-child(2)");
+ }
+
+ public BackgroundTaskItem openActions() {
+ elt.$(".js-task-action > .dropdown-toggle").click();
+ elt.$(".js-task-action > .dropdown-menu").shouldBe(visible);
+ return this;
+ }
+
+ public BackgroundTaskItem openScannerContext () {
+ elt.$(".js-task-show-scanner-context").click();
+ $(".js-task-scanner-context").shouldBe(visible);
+ return this;
+ }
+
+ public BackgroundTaskItem assertScannerContextContains(String text) {
+ $(".js-task-scanner-context").should(hasText(text));
+ 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;
+
+import com.codeborne.selenide.ElementsCollection;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.openqa.selenium.By;
+
+import static com.codeborne.selenide.Condition.exist;
+import static com.codeborne.selenide.Selenide.$;
+import static com.codeborne.selenide.Selenide.$$;
+
+public class BackgroundTasksPage {
+
+ public BackgroundTasksPage() {
+ $(By.cssSelector(".background-tasks")).should(exist);
+ }
+
+ public ElementsCollection getTasks() {
+ return $$(".background-tasks > tbody > tr");
+ }
+
+ public List<BackgroundTaskItem> getTasksAsItems() {
+ return getTasks()
+ .stream()
+ .map(BackgroundTaskItem::new)
+ .collect(Collectors.toList());
+ }
+}
return open(url, ProjectKeyPage.class);
}
+ public BackgroundTasksPage openBackgroundTasksPage() {
+ return open("/background_tasks", BackgroundTasksPage.class);
+ }
+
public void open(String relativeUrl) {
Selenide.open(relativeUrl);
}
return getJSON(url, data);
}
-export function getTask (id) {
+export function getTask (id, additionalFields) {
const url = '/api/ce/task';
- return getJSON(url, { id }).then(r => r.task);
+ return getJSON(url, { id, additionalFields }).then(r => r.task);
}
export function cancelTask (id) {
*/
import React from 'react';
import shallowCompare from 'react-addons-shallow-compare';
-
import TaskStatus from './TaskStatus';
import TaskComponent from './TaskComponent';
import TaskId from './TaskId';
import TaskDay from './TaskDay';
import TaskDate from './TaskDate';
import TaskExecutionTime from './TaskExecutionTime';
-import TaskCancelButton from './TaskCancelButton';
-import { STATUSES } from './../constants';
+import TaskActions from './TaskActions';
export default class Task extends React.Component {
static propTypes = {
return shallowCompare(this, nextProps, nextState);
}
- handleFilterTask (task, e) {
- e.preventDefault();
- this.props.onFilterTask(task);
- }
-
render () {
- const { task, index, tasks, component, types, onCancelTask } = this.props;
+ const { task, index, tasks, component, types, onCancelTask, onFilterTask } = this.props;
const prevTask = index > 0 ? tasks[index - 1] : null;
<TaskDate date={task.startedAt} baseDate={task.submittedAt} format="LTS"/>
<TaskDate date={task.executedAt} baseDate={task.submittedAt} format="LTS"/>
<TaskExecutionTime task={task}/>
-
- <td className="thin nowrap">
- {!component && (
- <a
- onClick={this.handleFilterTask.bind(this, task)}
- className="icon-filter icon-half-transparent spacer-left"
- href="#"
- title={`Show only "${task.componentName}" tasks`}
- data-toggle="tooltip"/>
- )}
- {task.status === STATUSES.PENDING && (
- <TaskCancelButton task={task} onCancelTask={onCancelTask}/>
- )}
- </td>
+ <TaskActions
+ component={component}
+ task={task}
+ onFilterTask={onFilterTask}
+ onCancelTask={onCancelTask}/>
</tr>
);
}
--- /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.
+ */
+import React from 'react';
+import shallowCompare from 'react-addons-shallow-compare';
+import ScannerContextView from '../views/ScannerContextView';
+import { STATUSES } from './../constants';
+import { translate } from '../../../helpers/l10n';
+
+export default class TaskActions extends React.Component {
+ shouldComponentUpdate (nextProps, nextState) {
+ return shallowCompare(this, nextProps, nextState);
+ }
+
+ handleFilterClick (e) {
+ e.preventDefault();
+ this.props.onFilterTask(this.props.task);
+ }
+
+ handleCancelClick (e) {
+ e.preventDefault();
+ this.props.onCancelTask(this.props.task);
+ }
+
+ handleShowScannerContextClick (e) {
+ e.preventDefault();
+ new ScannerContextView({ task: this.props.task }).render();
+ }
+
+ render () {
+ const { component, task } = this.props;
+
+ return (
+ <td className="thin nowrap">
+ <div className="dropdown js-task-action">
+ <button className="dropdown-toggle" data-toggle="dropdown">
+ <i className="icon-dropdown"/>
+ </button>
+ <ul className="dropdown-menu dropdown-menu-right">
+ {!component && (
+ <li>
+ <a className="js-task-filter" href="#" onClick={this.handleFilterClick.bind(this)}>
+ <i className="spacer-right icon-filter icon-half-transparent"/>
+ Show only {task.componentName} tasks
+ </a>
+ </li>
+ )}
+ {task.status === STATUSES.PENDING && (
+ <li>
+ <a className="js-task-cancel" href="#" onClick={this.handleCancelClick.bind(this)}>
+ <i className="spacer-right icon-delete"/>
+ {translate('background_tasks.cancel_task')}
+ </a>
+ </li>
+ )}
+ {task.hasScannerContext && (
+ <li>
+ <a className="js-task-show-scanner-context"
+ href="#"
+ onClick={this.handleShowScannerContextClick.bind(this)}>
+ {translate('background_tasks.show_scanner_context')}
+ </a>
+ </li>
+ )}
+ </ul>
+ </div>
+ </td>
+ );
+ }
+}
+++ /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.
- */
-import React from 'react';
-import { translate } from '../../../helpers/l10n';
-
-const TaskCancelButton = ({ task, onCancelTask }) => {
- function handleClick (e) {
- e.preventDefault();
- onCancelTask(task);
- }
-
- return (
- <a
- onClick={handleClick}
- className="spacer-left icon-delete"
- title={translate('background_tasks.cancel_task')}
- data-toggle="tooltip"
- href="#"/>
- );
-};
-
-export default TaskCancelButton;
--- /dev/null
+<form id="deactivate-user-form" autocomplete="off">
+ <div class="modal-head">
+ <h2>{{t 'background_tasks.scanner_context'}}: {{task.componentName}} [{{t 'background_task.type' task.type}}]</h2>
+ </div>
+ <div class="modal-body modal-container">
+ <div class="js-modal-messages"></div>
+
+ {{#if scannerContext}}
+ <pre class="js-task-scanner-context">{{scannerContext}}</pre>
+ {{else}}
+ <i class="spinner"></i>
+ {{/if}}
+ </div>
+ <div class="modal-foot">
+ <a href="#" class="js-modal-close">{{t 'close'}}</a>
+ </div>
+</form>
--- /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.
+ */
+import Modal from '../../../components/common/modals';
+import Template from './ScannerContextView.hbs';
+import { getTask } from '../../../api/ce';
+
+export default Modal.extend({
+ template: Template,
+ className: 'modal modal-large',
+
+ initialize () {
+ this.scannerContext = null;
+ this.loadScannerContext();
+ },
+
+ loadScannerContext() {
+ getTask(this.options.task.id, ['scannerContext']).then(task => {
+ this.scannerContext = task.scannerContext;
+ this.render();
+ });
+ },
+
+ serializeData() {
+ return {
+ task: this.options.task,
+ scannerContext: this.scannerContext
+ };
+ }
+});
+
background_tasks.logs=Logs
background_tasks.cancel_task=Cancel Task
background_tasks.cancel_all_tasks=Cancel All Pending Tasks
+background_tasks.scanner_context=Scanner Context
+background_tasks.show_scanner_context=Show Scanner Context
background_tasks.pending=pending
background_tasks.failures=still failing
background_tasks.in_progress_duration=Duration of the current task in progress.