import it.Category1Suite;
import org.junit.BeforeClass;
import org.junit.ClassRule;
+import org.junit.Rule;
import org.junit.Test;
import pageobjects.BackgroundTaskItem;
import pageobjects.BackgroundTasksPage;
public class BackgroundTasksTest {
@ClassRule
- public static Orchestrator orchestrator = Category1Suite.ORCHESTRATOR;
+ public static Orchestrator ORCHESTRATOR = Category1Suite.ORCHESTRATOR;
+
+ @Rule
+ public Navigation nav = Navigation.get(ORCHESTRATOR);
@BeforeClass
public static void beforeClass() {
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);
+ new SeleneseTest(selenese).runOn(ORCHESTRATOR);
}
@Test
public void display_scanner_context() {
- Navigation nav = Navigation.get(orchestrator);
- nav.openHomepage().logIn().submitCredentials("admin", "admin");
+ nav.logIn().submitCredentials("admin", "admin");
BackgroundTasksPage page = nav.openBackgroundTasksPage();
BackgroundTaskItem task = page.getTasksAsItems().get(0);
.assertScannerContextContains("Global properties:");
}
+ @Test
+ public void display_error_message_and_stacktrace() {
+ executeBuild("test-project", "Test Project", "2010-01-01");
+
+ nav.logIn().submitCredentials("admin", "admin");
+ BackgroundTasksPage page = nav.openBackgroundTasksPage();
+
+ BackgroundTaskItem task = page.getTasksAsItems().get(0);
+ task.openActions()
+ .openErrorStacktrace()
+ .assertErrorMessageContains("Date of analysis cannot be older than the date of the last known analysis")
+ .assertErrorStacktraceContains("Date of analysis cannot be older than the date of the last known analysis");
+ }
+
private static void executeBuild(String projectKey, String projectName) {
- orchestrator.executeBuild(
+ ORCHESTRATOR.executeBuild(
+ SonarScanner.create(projectDir("shared/xoo-sample"))
+ .setProjectKey(projectKey)
+ .setProjectName(projectName));
+ }
+
+ private static void executeBuild(String projectKey, String projectName, String date) {
+ ORCHESTRATOR.executeBuild(
SonarScanner.create(projectDir("shared/xoo-sample"))
.setProjectKey(projectKey)
.setProjectName(projectName)
- );
+ .setProperty("sonar.projectDate", date));
}
}
$(".js-task-scanner-context").should(hasText(text));
return this;
}
+
+ public BackgroundTaskItem openErrorStacktrace () {
+ elt.$(".js-task-show-stacktrace").click();
+ $(".js-task-error-message").shouldBe(visible);
+ return this;
+ }
+
+ public BackgroundTaskItem assertErrorMessageContains(String text) {
+ $(".js-task-error-message").should(hasText(text));
+ return this;
+ }
+
+ public BackgroundTaskItem assertErrorStacktraceContains(String text) {
+ $(".js-task-stacktrace").should(hasText(text));
+ return this;
+ }
}
import React from 'react';
import shallowCompare from 'react-addons-shallow-compare';
import ScannerContextView from '../views/ScannerContextView';
+import StacktraceView from '../views/StacktraceView';
import { STATUSES } from './../constants';
-import { translate } from '../../../helpers/l10n';
+import { translate, translateWithParameters } from '../../../helpers/l10n';
export default class TaskActions extends React.Component {
shouldComponentUpdate (nextProps, nextState) {
new ScannerContextView({ task: this.props.task }).render();
}
+ handleShowStacktraceClick (e) {
+ e.preventDefault();
+ new StacktraceView({ task: this.props.task }).render();
+ }
+
render () {
const { component, task } = this.props;
+ const canFilter = component == null;
+ const canCancel = task.status === STATUSES.PENDING;
+ const canShowStacktrace = task.errorMessage != null;
+ const hasActions = canFilter || canCancel || task.hasScannerContext || canShowStacktrace;
+
+ if (!hasActions) {
+ return <td> </td>;
+ }
+
return (
<td className="thin nowrap">
<div className="dropdown js-task-action">
<i className="icon-dropdown"/>
</button>
<ul className="dropdown-menu dropdown-menu-right">
- {!component && (
+ {canFilter && (
<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
+ <i className="spacer-right icon-filter icon-gray"/>
+ {translateWithParameters('background_tasks.filter_by_component_x', task.componentName)}
</a>
</li>
)}
- {task.status === STATUSES.PENDING && (
+ {canCancel && (
<li>
<a className="js-task-cancel" href="#" onClick={this.handleCancelClick.bind(this)}>
<i className="spacer-right icon-delete"/>
<a className="js-task-show-scanner-context"
href="#"
onClick={this.handleShowScannerContextClick.bind(this)}>
+ <i className="spacer-right icon-list icon-gray"/>
{translate('background_tasks.show_scanner_context')}
</a>
</li>
)}
+ {canShowStacktrace && (
+ <li>
+ <a className="js-task-show-stacktrace"
+ href="#"
+ onClick={this.handleShowStacktraceClick.bind(this)}>
+ <i className="spacer-right icon-list icon-red"/>
+ {translate('background_tasks.show_stacktrace')}
+ </a>
+ </li>
+ )}
</ul>
</div>
</td>
--- /dev/null
+<form id="deactivate-user-form" autocomplete="off">
+ <div class="modal-head">
+ <h2>{{t 'background_tasks.error_stacktrace'}}: {{task.componentName}} [{{t 'background_task.type' task.type}}]</h2>
+ </div>
+ <div class="modal-body modal-container">
+ <div class="js-modal-messages"></div>
+
+ {{#if loaded}}
+ <h4 class="spacer-bottom">{{t 'background_tasks.error_message'}}</h4>
+ <pre class="js-task-error-message">{{task.errorMessage}}</pre>
+
+ {{#if stacktrace}}
+ <h4 class="huge-spacer-top spacer-bottom">{{t 'background_tasks.error_stacktrace'}}</h4>
+ <pre class="js-task-stacktrace">{{stacktrace}}</pre>
+ {{/if}}
+ {{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 './StacktraceView.hbs';
+import { getTask } from '../../../api/ce';
+
+export default Modal.extend({
+ template: Template,
+ className: 'modal modal-large',
+
+ initialize () {
+ this.loaded = false;
+ this.stacktrace = null;
+ this.loadStacktrace();
+ },
+
+ loadStacktrace() {
+ getTask(this.options.task.id, ['stacktrace']).then(task => {
+ this.loaded = true;
+ this.stacktrace = task.errorStacktrace;
+ this.render();
+ });
+ },
+
+ serializeData() {
+ return {
+ task: this.options.task,
+ stacktrace: this.stacktrace,
+ loaded: this.loaded
+ };
+ }
+});
+
.icon-black { color: @baseFontColor; }
+.icon-gray { color: #999; }
+.icon-gray path { fill: #999; }
.icon-red { color: @red; }
.icon-green { color: @green; }
.icon-color-link { color: @darkBlue; }
background_tasks.table.duration=Duration
background_tasks.logs=Logs
+background_tasks.filter_by_component_x=Filter by Component "{0}"
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.show_stacktrace=Show Error Stacktrace
+background_tasks.error_message=Error Message
+background_tasks.error_stacktrace=Error Stacktrace
background_tasks.pending=pending
background_tasks.failures=still failing
background_tasks.in_progress_duration=Duration of the current task in progress.