]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-7846 Display error of a failing CE task in the background page 1157/head
authorStas Vilchik <vilchiks@gmail.com>
Mon, 22 Aug 2016 13:15:28 +0000 (15:15 +0200)
committerStas Vilchik <vilchiks@gmail.com>
Tue, 23 Aug 2016 10:08:51 +0000 (12:08 +0200)
it/it-tests/src/test/java/it/projectAdministration/BackgroundTasksTest.java
it/it-tests/src/test/java/pageobjects/BackgroundTaskItem.java
server/sonar-web/src/main/js/apps/background-tasks/components/TaskActions.js
server/sonar-web/src/main/js/apps/background-tasks/views/StacktraceView.hbs [new file with mode: 0644]
server/sonar-web/src/main/js/apps/background-tasks/views/StacktraceView.js [new file with mode: 0644]
server/sonar-web/src/main/less/init/icons.less
sonar-core/src/main/resources/org/sonar/l10n/core.properties

index 1a7f8795b055ad322c1f3fcbc7a880891e02ade9..a27df0b50158b6b2ef2a6d47ebada26e6b5645e1 100644 (file)
@@ -25,6 +25,7 @@ import com.sonar.orchestrator.selenium.Selenese;
 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;
@@ -36,7 +37,10 @@ import static util.ItUtils.projectDir;
 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() {
@@ -49,13 +53,12 @@ public class BackgroundTasksTest {
     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);
@@ -65,11 +68,32 @@ public class BackgroundTasksTest {
       .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));
   }
 }
index e749c9c54e6108631e0842aca4fc0bbb3ef2c659..75ea69654681719f9ef36da985d2859ba66d87db 100644 (file)
@@ -53,4 +53,20 @@ public class BackgroundTaskItem {
     $(".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;
+  }
 }
index 82be5d2387076de8cebdc6409196578d7277f380..d6de9d26c63ac6b6cc702c451c928faf0172774b 100644 (file)
@@ -20,8 +20,9 @@
 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) {
@@ -43,9 +44,23 @@ export default class TaskActions extends React.Component {
     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>&nbsp;</td>;
+    }
+
     return (
         <td className="thin nowrap">
           <div className="dropdown js-task-action">
@@ -53,15 +68,15 @@ export default class TaskActions extends React.Component {
               <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"/>
@@ -74,10 +89,21 @@ export default class TaskActions extends React.Component {
                     <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>
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/views/StacktraceView.hbs b/server/sonar-web/src/main/js/apps/background-tasks/views/StacktraceView.hbs
new file mode 100644 (file)
index 0000000..1a4d2eb
--- /dev/null
@@ -0,0 +1,23 @@
+<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>
diff --git a/server/sonar-web/src/main/js/apps/background-tasks/views/StacktraceView.js b/server/sonar-web/src/main/js/apps/background-tasks/views/StacktraceView.js
new file mode 100644 (file)
index 0000000..809467d
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * 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
+    };
+  }
+});
+
index 86dc018eeda8d76e20f7405a81109fba3a106ea3..4cf3404984fa523b1f61e8399dbe108d3aab2683 100644 (file)
@@ -62,6 +62,8 @@ a[class^="icon-"], a[class*=" icon-"] {
 
 
 .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; }
index d68b0ad98d7d33314d7a5a553a58b4eeb0f82dc3..e9c4b7d03797da774ad529619ffbd2b36e261266 100644 (file)
@@ -2972,10 +2972,14 @@ background_tasks.table.finished=Finished
 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.