]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5966 improve display of issue boxes
authorStas Vilchik <vilchiks@gmail.com>
Thu, 22 Jan 2015 13:45:38 +0000 (14:45 +0100)
committerStas Vilchik <vilchiks@gmail.com>
Thu, 22 Jan 2015 16:01:43 +0000 (17:01 +0100)
14 files changed:
server/sonar-web/src/main/coffee/issue/issue-view.coffee
server/sonar-web/src/main/coffee/issue/views/assign-form-view.coffee
server/sonar-web/src/main/coffee/issue/views/set-severity-form-view.coffee
server/sonar-web/src/main/coffee/issue/views/tags-form-view.coffee [new file with mode: 0644]
server/sonar-web/src/main/coffee/issue/views/transitions-form-view.coffee [new file with mode: 0644]
server/sonar-web/src/main/coffee/issues/component-viewer/main.coffee
server/sonar-web/src/main/coffee/issues/workspace-list-view.coffee
server/sonar-web/src/main/hbs/issue/issue-tags-form-option.hbs [new file with mode: 0644]
server/sonar-web/src/main/hbs/issue/issue-tags-form.hbs [new file with mode: 0644]
server/sonar-web/src/main/hbs/issue/issue-transitions-form.hbs [new file with mode: 0644]
server/sonar-web/src/main/hbs/issue/issue.hbs
server/sonar-web/src/main/hbs/issues/issues-help.hbs
server/sonar-web/src/main/js/tests/e2e/tests/issues-page-spec.js
server/sonar-web/src/main/less/components/issues.less

index 669179a6648649209f16671fad40544e7394a77f..a8e09a3ca4d45553389d4a250ab361a2ce28ed41 100644 (file)
@@ -6,12 +6,14 @@ define [
 
   'issue/views/issue-popup'
 
+  'issue/views/transitions-form-view'
   'issue/views/assign-form-view'
   'issue/views/comment-form-view'
   'issue/views/plan-form-view'
   'issue/views/set-severity-form-view'
   'issue/views/more-actions-view'
   'issue/views/rule-overlay'
+  'issue/views/tags-form-view'
 
   'templates/issue'
 
@@ -23,12 +25,14 @@ define [
 
   IssuePopup
 
+  TransitionsFormView
   AssignFormView
   CommentFormView
   PlanFormView
   SetSeverityFormView
   MoreActionsView
   RuleOverlay
+  TagsFormView
 
 ) ->
 
@@ -66,9 +70,7 @@ define [
       'click .js-issue-show-changelog': 'showChangeLog'
       'click .js-issue-more': 'showMoreActions'
       'click .js-issue-rule': 'showRule'
-      'click @ui.tagsChange': 'changeTags'
-      'click @ui.tagsEditDone': 'editDone'
-      'click @ui.tagsEditCancel': 'cancelEdit'
+      'click .js-issue-edit-tags': 'editTags'
 
 
     onRender: ->
@@ -172,22 +174,19 @@ define [
         .done =>
           @updateAfterAction true
           window.process.finishBackgroundProcess p
-        .fail (r) =>
+        .fail =>
           window.process.failBackgroundProcess p
 
 
     transition: (e) ->
-      p = window.process.addBackgroundProcess()
-      $.ajax
-        type: 'POST',
-        url: baseUrl + '/api/issues/do_transition',
-        data:
-          issue: @model.get('key')
-          transition: $(e.currentTarget).data 'transition'
-      .done =>
-        @resetIssue {}, p
-      .fail =>
-        window.process.failBackgroundProcess p
+      e.stopPropagation()
+      $('body').click()
+      @popup = new TransitionsFormView
+        triggerEl: $(e.currentTarget)
+        bottom: true
+        model: @model
+        view: @
+      @popup.render()
 
 
     setSeverity: (e) ->
@@ -267,6 +266,16 @@ define [
         ruleOverlay.render()
 
 
+    editTags: (e)->
+      e.stopPropagation()
+      $('body').click()
+      @popup = new TagsFormView
+        triggerEl: $(e.currentTarget)
+        bottomRight: true
+        model: @model
+      @popup.render()
+
+
     changeTags: ->
       p = window.process.addBackgroundProcess()
       jQuery.ajax
index ebff3de408ad50b65e08b6fe243a414c4cfed714..bca4eb2d6417301b1814286d10bee6bdf39a0c17 100644 (file)
@@ -36,11 +36,11 @@ define [
 
     onRender: ->
       super
-      @renderAssignees()
+      @renderTags()
       setTimeout (=> @$('input').focus()), 100
 
 
-    renderAssignees: ->
+    renderTags: ->
       @$('.issue-action-option').remove()
       @getAssignees().forEach @renderAssignee, @
       @selectInitialOption()
@@ -118,7 +118,7 @@ define [
       @assignees = users.map (user) ->
         id: user.login
         text: user.name
-      @renderAssignees()
+      @renderTags()
 
 
     getAssignees: ->
index b8110244861e2fd19946792c20aab3381b558027..836ef3babaa39db4024da90e1b23bd6e8b2f4841 100644 (file)
@@ -12,12 +12,12 @@ define [
     template: Templates['issue-set-severity-form']
 
 
-    getSeverity: ->
+    getTransition: ->
       @model.get 'severity'
 
 
     selectInitialOption: ->
-      @makeActive @getOptions().filter("[data-value=#{@getSeverity()}]")
+      @makeActive @getOptions().filter("[data-value=#{@getTransition()}]")
 
 
     selectOption: (e) ->
@@ -27,7 +27,7 @@ define [
 
 
     submit: (severity) ->
-      _severity = @getSeverity()
+      _severity = @getTransition()
       return if severity == _severity
       p = window.process.addBackgroundProcess()
       @model.set severity: severity
diff --git a/server/sonar-web/src/main/coffee/issue/views/tags-form-view.coffee b/server/sonar-web/src/main/coffee/issue/views/tags-form-view.coffee
new file mode 100644 (file)
index 0000000..6b32590
--- /dev/null
@@ -0,0 +1,145 @@
+define [
+  'issue/views/action-options-view'
+  'templates/issue'
+], (
+  ActionOptionsView
+) ->
+
+  $ = jQuery
+
+
+  class extends ActionOptionsView
+    template: Templates['issue-tags-form']
+    optionTemplate: Templates['issue-tags-form-option']
+
+
+    modelEvents:
+      'change:tags': 'renderTags'
+
+
+    events: ->
+      _.extend super,
+        'click input': 'onInputClick'
+        'keydown input': 'onInputKeydown'
+        'keyup input': 'onInputKeyup'
+
+
+    initialize: ->
+      super
+      @query = ''
+      @tags = []
+      @selected = 0
+      @debouncedSearch = _.debounce @search, 250
+      @requestTags()
+
+
+    requestTags: ->
+      $.get "#{baseUrl}/api/issues/tags", ps: 0
+      .done (data) =>
+        @tags = data.tags
+        @renderTags()
+
+
+    onRender: ->
+      super
+      @renderTags()
+      setTimeout (=> @$('input').focus()), 100
+
+
+    selectInitialOption: ->
+      @selected = Math.max Math.min(@selected, @getOptions().length - 1), 0
+      @makeActive @getOptions().eq @selected
+
+
+    filterTags: (tags) ->
+      _.filter tags, (tag) => tag.indexOf(@query) != -1
+
+
+    renderTags: ->
+      @$('.issue-action-option').remove()
+      @filterTags(@getTags()).forEach @renderSelectedTag, @
+      @filterTags(_.difference(@tags, @getTags())).forEach @renderTag, @
+      if @query.length > 0 && @tags.indexOf(@query) == -1 && @getTags().indexOf(@query) == -1
+        @renderCustomTag @query
+      @selectInitialOption()
+
+
+    renderSelectedTag: (tag) ->
+      html = @optionTemplate { tag: tag, selected: true, custom: false }
+      @$('.issue-action-options').append html
+
+
+    renderTag: (tag) ->
+      html = @optionTemplate { tag: tag, selected: false, custom: false }
+      @$('.issue-action-options').append html
+
+
+    renderCustomTag: (tag) ->
+      html = @optionTemplate { tag: tag, selected: false, custom: true }
+      @$('.issue-action-options').append html
+
+
+    selectOption: (e) ->
+      e.preventDefault()
+      e.stopPropagation()
+      tags = @getTags().slice()
+      tag = $(e.currentTarget).data 'value'
+      if $(e.currentTarget).data('selected')?
+        tags = _.without tags, tag
+      else
+        tags.push tag
+      @selected = @getOptions().index $(e.currentTarget)
+      @submit tags
+
+
+    submit: (tags) ->
+      _tags = @getTags()
+      @model.set tags: tags
+      p = window.process.addBackgroundProcess()
+      $.ajax
+        type: 'POST'
+        url: "#{baseUrl}/api/issues/set_tags"
+        data:
+          key: @model.id
+          tags: tags.join()
+      .done =>
+        window.process.finishBackgroundProcess p
+      .fail =>
+        @model.set tags: _tags
+        window.process.failBackgroundProcess p
+
+
+    onInputClick: (e) ->
+      e.stopPropagation()
+
+
+    onInputKeydown: (e) ->
+      @query = @$('input').val()
+      return @selectPreviousOption() if e.keyCode == 38 # up
+      return @selectNextOption() if e.keyCode == 40 # down
+      return @selectActiveOption() if e.keyCode == 13 # return
+      return false if e.keyCode == 9 # tab
+      @close() if e.keyCode == 27 # escape
+
+
+    onInputKeyup: ->
+      query = @$('input').val()
+      if query != @query
+        @query = query
+        @debouncedSearch query
+
+
+    search: (query) ->
+      @query = query
+      @renderTags()
+
+
+    resetAssignees: (users) ->
+      @assignees = users.map (user) ->
+        id: user.login
+        text: user.name
+      @renderTags()
+
+
+    getTags: ->
+      @model.get('tags') || []
diff --git a/server/sonar-web/src/main/coffee/issue/views/transitions-form-view.coffee b/server/sonar-web/src/main/coffee/issue/views/transitions-form-view.coffee
new file mode 100644 (file)
index 0000000..f389cbf
--- /dev/null
@@ -0,0 +1,36 @@
+define [
+  'issue/views/action-options-view'
+  'templates/issue'
+], (
+  ActionOptionsView
+) ->
+
+  $ = jQuery
+  
+
+  class extends ActionOptionsView
+    template: Templates['issue-transitions-form']
+
+
+    selectInitialOption: ->
+      @makeActive @getOptions().first()
+
+
+    selectOption: (e) ->
+      transition = $(e.currentTarget).data 'value'
+      @submit transition
+      super
+
+
+    submit: (transition) ->
+      p = window.process.addBackgroundProcess()
+      $.ajax
+        type: 'POST',
+        url: baseUrl + '/api/issues/do_transition',
+        data:
+          issue: @model.get('key')
+          transition: transition
+      .done =>
+        @options.view.resetIssue {}, p
+      .fail =>
+        window.process.failBackgroundProcess p
index 9979b1ad5ba30d2b8686293334c9cc95c40116bf..09b1dc11b18c6def18083b66c626f90dc749cb54 100644 (file)
@@ -36,11 +36,6 @@ define [
 
 
     bindShortcuts: ->
-      doTransition = (transition) =>
-        selectedIssueView = @getSelectedIssueEl()
-        return unless selectedIssueView
-        selectedIssueView.find("[data-transition=#{transition}]").click()
-
       doAction = (action) =>
         selectedIssueView = @getSelectedIssueEl()
         return unless selectedIssueView
@@ -58,12 +53,7 @@ define [
         @options.app.controller.closeComponentViewer()
         false
 
-      key 'c', 'componentViewer', -> doTransition 'confirm'
-      key 'c', 'componentViewer', -> doTransition 'unconfirm'
-      key 'u', 'componentViewer', -> doTransition 'mute'
-      key 'r', 'componentViewer', -> doTransition 'resolve'
-      key 'r', 'componentViewer', -> doTransition 'reopen'
-      key 'f', 'componentViewer', -> doTransition 'falsepositive'
+      key 'f', 'componentViewer', -> doAction 'transition'
       key 'a', 'componentViewer', -> doAction 'assign'
       key 'm', 'componentViewer', -> doAction 'assign-to-me'
       key 'p', 'componentViewer', -> doAction 'plan'
index b659fcf1b6ffcf46828e2188e200c8a97c612c4e..ebe828753674d809bbfd059bc64d9e04155edb54 100644 (file)
@@ -25,12 +25,6 @@ define [
 
 
     bindShortcuts: ->
-      doTransition = (transition) =>
-        selectedIssue = @collection.at @options.app.state.get 'selectedIndex'
-        return unless selectedIssue?
-        selectedIssueView = @children.findByModel selectedIssue
-        selectedIssueView.$("[data-transition=#{transition}]").click()
-
       doAction = (action) =>
         selectedIssue = @collection.at @options.app.state.get 'selectedIndex'
         return unless selectedIssue?
@@ -44,12 +38,7 @@ define [
         @options.app.controller.showComponentViewer selectedIssue
         return false
 
-      key 'c', 'list', -> doTransition 'confirm'
-      key 'c', 'list', -> doTransition 'unconfirm'
-      key 'u', 'list', -> doTransition 'mute'
-      key 'r', 'list', -> doTransition 'resolve'
-      key 'r', 'list', -> doTransition 'reopen'
-      key 'f', 'list', -> doTransition 'falsepositive'
+      key 'f', 'list', -> doAction 'transition'
       key 'a', 'list', -> doAction 'assign'
       key 'm', 'list', -> doAction 'assign-to-me'
       key 'p', 'list', -> doAction 'plan'
diff --git a/server/sonar-web/src/main/hbs/issue/issue-tags-form-option.hbs b/server/sonar-web/src/main/hbs/issue/issue-tags-form-option.hbs
new file mode 100644 (file)
index 0000000..df054fe
--- /dev/null
@@ -0,0 +1,15 @@
+<a href="#" class="issue-action-option" data-value="{{tag}}" data-text="{{tag}}"
+   {{#if selected}}data-selected{{/if}}>
+
+  {{#if selected}}
+    <i class="icon-checkbox icon-checkbox-checked"></i>
+  {{else}}
+    <i class="icon-checkbox"></i>
+  {{/if}}
+
+  {{#if custom}}
+    + {{tag}}
+  {{else}}
+    {{tag}}
+  {{/if}}
+</a>
diff --git a/server/sonar-web/src/main/hbs/issue/issue-tags-form.hbs b/server/sonar-web/src/main/hbs/issue/issue-tags-form.hbs
new file mode 100644 (file)
index 0000000..9d20be8
--- /dev/null
@@ -0,0 +1,6 @@
+<div class="issue-action-options">
+  <i class="icon-search issue-action-options-search-icon"></i>
+  <input class="issue-action-options-search" type="text" placeholder="Search..." value="{{query}}">
+</div>
+
+<div class="bubble-popup-arrow"></div>
diff --git a/server/sonar-web/src/main/hbs/issue/issue-transitions-form.hbs b/server/sonar-web/src/main/hbs/issue/issue-transitions-form.hbs
new file mode 100644 (file)
index 0000000..ab64424
--- /dev/null
@@ -0,0 +1,9 @@
+<div class="issue-action-options">
+  {{#each transitions}}
+    <a href="#" class="issue-action-option js-issue-transition" data-value="{{this}}">
+      {{t 'issue.transition' this}}
+    </a>
+  {{/each}}
+</div>
+
+<div class="bubble-popup-arrow"></div>
index bf36dc75dc89e3455030ec0695d95a59c4f0da8b..0c23a0576c4d039a5ae34d719c6351f4b5b2e750 100644 (file)
@@ -2,14 +2,13 @@
   <tr>
     <td>
       <div class="issue-message">{{message}}</div>
+      <div class="issue-message">
+        <a class="issue-rule js-issue-rule">{{ruleName}}</a>
+      </div>
     </td>
 
     <td class="issue-table-meta-cell">
       <div class="issue-meta-list">
-        <div class="issue-meta">
-          <a class="issue-action js-issue-rule">Rule</a>
-        </div>
-
         {{#if debt}}
           <div class="issue-meta">
             <span class="issue-meta-label">{{t 'issue.debt'}} {{debt}}</span>
         </div>
 
         <div class="issue-meta">
-          {{statusHelper status resolution}}
+          {{#notEmpty transitions}}
+            <a class="issue-action issue-action-with-options js-issue-transition">
+              <span class="issue-meta-label">{{statusHelper status resolution}}</span>&nbsp;<i
+                class="icon-dropdown"></i>
+            </a>
+          {{else}}
+            {{statusHelper status resolution}}
+          {{/notEmpty}}
         </div>
 
-        {{#notEmpty transitions}}
-          <div class="issue-meta">
-            {{#each transitions}}
-              <a class="issue-action js-issue-transition" data-transition="{{this}}">
-                <span class="issue-meta-label">{{t 'issue.transition' this}}</span>
-              </a>
-            {{/each}}
-          </div>
-        {{/notEmpty}}
-
         <div class="issue-meta">
           {{#inArray actions "assign"}}
             <a class="issue-action issue-action-with-options js-issue-assign">
@@ -97,7 +93,7 @@
         {{#inArray actions "comment"}}
           <div class="issue-meta">
             <a class="issue-action js-issue-comment"><span
-                class="issue-meta-label">{{t 'issue.comment.formlink' }}</span></a>
+                class="issue-meta-label"><i class="icon-comment"></i></span></a>
           </div>
         {{/inArray}}
 
     </td>
 
     <td class="issue-table-meta-cell">
-      <div class="issue-tags">
-        <span class="issue-tag-list {{#inArray actions "set_tags"}}js-issue-edit-tags{{/inArray}}">
-          <i class="icon-tags"></i>
-          <span class="issue-meta">{{#if tags}}{{join tags ', '}}{{else}}{{t 'issue.no_tag'}}{{/if}}</span>
-        </span>
+      <div class="issue-meta js-issue-tags">
         {{#inArray actions "set_tags"}}
-          <span class="issue-meta issue-tag-edit">
-            <input class="issue-tag-input" type="text" value="{{#if tags}}{{join tags ','}}{{/if}}">
-            <span class="button-group">
-              <button class="issue-tag-edit-done">{{t 'Done'}}</button>
-            </span>
-            <a class="issue-tag-edit-cancel">{{t 'cancel'}}</a>
+          <a class="issue-action issue-action-with-options js-issue-edit-tags">
+            <span>
+              <i class="icon-tags"></i>&nbsp;<span>{{#if tags}}{{join tags ', '}}{{else}}{{t 'issue.no_tag'}}{{/if}}</span>
+            </span>&nbsp;<i class="icon-dropdown"></i>
+          </a>
+        {{else}}
+          <span>
+            <i class="icon-tags"></i>&nbsp;<span>{{#if tags}}{{join tags ', '}}{{else}}{{t 'issue.no_tag'}}{{/if}}</span>
           </span>
         {{/inArray}}
       </div>
index fe36051338e297347789672eab133f16385cd1e4..1fc3cdcc65c0f6ee0d0163e3e4d5f5b5844fdd3e 100644 (file)
   </ul>
   <p>To control selected issue</p>
   <ul>
-    <li><span class="shortcut-button">c</span> &nbsp;&nbsp; to confirm/unconfirm</li>
-    <li><span class="shortcut-button">r</span> &nbsp;&nbsp; to resolve/reopen</li>
-    <li><span class="shortcut-button">f</span> &nbsp;&nbsp; to mark as false positive</li>
-    <li><span class="shortcut-button">u</span> &nbsp;&nbsp; to mute</li>
+    <li><span class="shortcut-button">f</span> &nbsp;&nbsp; to do a transition</li>
     <li><span class="shortcut-button">a</span> &nbsp;&nbsp; to assign</li>
     <li><span class="shortcut-button">m</span> &nbsp;&nbsp; to assign to the current user</li>
     <li><span class="shortcut-button">p</span> &nbsp;&nbsp; to plan</li>
index 710a306846204bea6cf58e8cc12445c186872466..1fc62c032d6346bba2b37d0983ec49a5ec4ee3fa 100644 (file)
@@ -79,15 +79,13 @@ casper.test.begin(testName('Issue Box', 'Check Elements'), function (test) {
 
       .then(function () {
         test.assertSelectorContains('.issue.selected', "Add a 'package-info.java' file to document the");
-        test.assertExists('.issue.selected .issue-tags');
-        test.assertSelectorContains('.issue.selected .issue-tags', 'issue.no_tag');
+        test.assertExists('.issue.selected .js-issue-tags');
+        test.assertSelectorContains('.issue.selected .js-issue-tags', 'issue.no_tag');
         test.assertExists('.issue.selected .js-issue-set-severity');
         test.assertSelectorContains('.issue.selected .js-issue-set-severity', 'MAJOR');
         test.assertSelectorContains('.issue.selected', 'CONFIRMED');
-        test.assertElementCount('.issue.selected .js-issue-transition', 3);
-        test.assertExists('.issue.selected [data-transition=unconfirm]');
-        test.assertExists('.issue.selected [data-transition=resolve]');
-        test.assertExists('.issue.selected [data-transition=falsepositive]');
+        test.assertElementCount('.issue.selected .js-issue-transition', 1);
+        test.assertExists('.issue.selected .js-issue-transition');
         test.assertExists('.issue.selected .js-issue-assign');
         test.assertSelectorContains('.issue.selected .js-issue-assign', 'unassigned');
         test.assertExists('.issue.selected .js-issue-plan');
@@ -111,25 +109,31 @@ casper.test.begin(testName('Issue Box', 'Tags'), function (test) {
         lib.mockRequest('/api/l10n/index', '{}');
         lib.mockRequestFromFile('/api/issue_filters/app', 'app.json');
         lib.mockRequestFromFile('/api/issues/search', 'search-with-tags.json');
-        this.showMock = lib.mockRequestFromFile('/api/issues/show*', 'show-with-tags.json');
+        lib.mockRequestFromFile('/api/issues/tags', 'tags.json');
+        lib.mockRequestFromFile('/api/issues/set_tags', 'tags-modified.json');
       })
 
       .then(function () {
-        casper.waitForSelector('.issue.selected .issue-tags', function () {
-          test.assertSelectorContains('.issue.selected .issue-tags', 'security, cwe');
-          lib.mockRequestFromFile('/api/issues/tags*', 'tags.json');
-          casper.click('.issue.selected .issue-tag-list');
-
-          casper.waitForSelector('.issue.selected .select2-input', function () {
-            lib.mockRequestFromFile('/api/issues/set_tags', 'tags-modified.json');
-            casper.click('.issue.selected .issue-tag-edit-done');
-            casper.waitWhileVisible('.issue.selected .issue-tag-edit');
-            casper.waitUntilVisible('.issue.selected .issue-tag-list', function () {
-              // TODO Find a way to have this assertion work
-              // test.assertSelectorContains('.issue.selected .issue-tags .issue-tag-list', 'security, cwe, cert');
-            });
-          });
-        });
+        casper.waitForSelector('.issue.selected .js-issue-tags');
+      })
+
+      .then(function () {
+        test.assertSelectorContains('.issue.selected .js-issue-tags', 'security, cwe');
+        casper.click('.issue.selected .js-issue-edit-tags');
+      })
+
+      .then(function () {
+        casper.waitForSelector('.issue-action-option[data-value=design]');
+      })
+
+      .then(function () {
+        casper.click('.issue-action-option[data-value=design]');
+        test.assertSelectorContains('.issue.selected .js-issue-tags', 'security, cwe, design');
+      })
+
+      .then(function () {
+        casper.click('.issue-action-option[data-value=cwe]');
+        test.assertSelectorContains('.issue.selected .js-issue-tags', 'security, design');
       })
 
       .run(function () {
@@ -146,54 +150,23 @@ casper.test.begin(testName('Issue Box', 'Transitions'), function (test) {
         lib.mockRequest('/api/l10n/index', '{}');
         lib.mockRequestFromFile('/api/issue_filters/app', 'app.json');
         lib.mockRequestFromFile('/api/issues/search', 'search.json');
-        this.showMock = lib.mockRequestFromFile('/api/issues/show*', 'show.json');
+        lib.mockRequestFromFile('/api/issues/show*', 'show.json');
         lib.mockRequest('/api/issues/do_transition', '{}');
       })
 
       .then(function () {
-        casper.waitForSelector('.issue.selected [data-transition=unconfirm]', function () {
-          test.assertExists('.issue.selected [data-transition=unconfirm]');
-          test.assertExists('.issue.selected [data-transition=resolve]');
-          test.assertExists('.issue.selected [data-transition=falsepositive]');
-          lib.clearRequestMock(this.showMock);
-          this.showMock = lib.mockRequestFromFile('/api/issues/show*', 'show-open.json');
-          casper.click('.issue.selected [data-transition=unconfirm]');
-        });
-      })
-
-      .then(function () {
-        casper.waitForSelector('.issue.selected [data-transition=confirm]', function () {
-          test.assertExists('.issue.selected [data-transition=resolve]');
-          test.assertExists('.issue.selected [data-transition=falsepositive]');
-          lib.clearRequestMock(this.showMock);
-          this.showMock = lib.mockRequestFromFile('/api/issues/show*', 'show-resolved.json');
-          casper.click('.issue.selected [data-transition=resolve]');
-        });
-      })
-
-      .then(function () {
-        casper.waitForSelector('.issue.selected [data-transition=reopen]', function () {
-          lib.clearRequestMock(this.showMock);
-          this.showMock = lib.mockRequestFromFile('/api/issues/show*', 'show-open.json');
-          casper.click('.issue.selected [data-transition=reopen]');
-        });
+        casper.waitForSelector('.issue.selected .js-issue-transition');
       })
 
       .then(function () {
-        casper.waitForSelector('.issue.selected [data-transition=confirm]', function () {
-          test.assertExists('.issue.selected [data-transition=confirm]');
-          test.assertExists('.issue.selected [data-transition=resolve]');
-          test.assertExists('.issue.selected [data-transition=falsepositive]');
-          lib.clearRequestMock(this.showMock);
-          this.showMock = lib.mockRequestFromFile('/api/issues/show*', 'show-resolved.json');
-          casper.click('.issue.selected [data-transition=falsepositive]');
-        });
+        casper.click('.issue.selected .js-issue-transition');
+        casper.waitForSelector('.issue-action-option');
       })
 
       .then(function () {
-        casper.waitForSelector('.issue.selected [data-transition=reopen]', function () {
-          test.assertExists('.issue.selected [data-transition=reopen]');
-        });
+        test.assertExists('.issue-action-option[data-value=unconfirm]');
+        test.assertExists('.issue-action-option[data-value=resolve]');
+        test.assertExists('.issue-action-option[data-value=falsepositive]');
       })
 
       .run(function () {
index e4029858dde3fce397999bba111e4427f78d2039..6db3500bfb8cb64f21a9d4b237aa70d9f5b1a87e 100644 (file)
   font-weight: 500;
 }
 
-.issue-tags {
-  padding-left: @leftPadding;
-  line-height: 1.5;
-  font-size: @baseFontSize;
-}
-
-.issue-tag-list {
-  display: inline-block;
-
-  .icon-tags {
-    color: @secondFontColor;
-  }
-}
-
-.issue-tags-change {
-  cursor: pointer;
-}
-
-.issue-tag-edit {
-  display: none;
-}
-
-.issue-tag-edit-cancel {
-  vertical-align: middle;
+.issue-rule {
+  font-size: 11px;
+  font-weight: 400;
+  .link-no-underline;
 }
 
 .issue-component {