aboutsummaryrefslogtreecommitdiffstats
path: root/server/sonar-web/src
diff options
context:
space:
mode:
authorStas Vilchik <vilchiks@gmail.com>2015-05-26 15:58:33 +0200
committerStas Vilchik <vilchiks@gmail.com>2015-05-28 09:48:55 +0200
commit456991a9a6292cfd88b053d808066f541e847028 (patch)
tree9b5161ae04622a4498a162916077ebcf2bc82bd3 /server/sonar-web/src
parent2a9e0c6bbfc7e3403d353f0373d302b7ecd32706 (diff)
downloadsonarqube-456991a9a6292cfd88b053d808066f541e847028.tar.gz
sonarqube-456991a9a6292cfd88b053d808066f541e847028.zip
refactor quality gates page
Diffstat (limited to 'server/sonar-web/src')
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/app.coffee118
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/collections/conditions.coffee29
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/collections/quality-gates.coffee48
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/layout.coffee42
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/models/condition.coffee58
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/models/quality-gate.coffee26
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/router.coffee62
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-condition.hbs62
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-conditions-empty.hbs3
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-conditions.hbs45
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-edit.hbs22
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-sidebar-list-empty.hbs1
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-actions-view.coffee44
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-condition-view.coffee146
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-conditions-empty-view.coffee27
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-conditions-view.coffee105
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-header-view.coffee101
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-projects-view.coffee55
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-view.coffee67
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-edit-view.coffee101
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-empty-view.coffee28
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-item-view.coffee44
-rw-r--r--server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-view.coffee42
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/actions-view.js27
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/app.js69
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/condition.js47
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/conditions.js10
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/controller.js65
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/copy-view.js24
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/create-view.js24
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/delete-view.js33
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/details-view.js46
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/form-view.js47
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/gate-condition-view.js100
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-delete-view.js39
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-empty-view.js16
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-view.js75
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/gate-projects-view.js48
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/gate-view.js29
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/gate.js54
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/gates-view.js25
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/gates.js30
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/header-view.js52
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/layout.js24
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/rename-view.js23
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/router.js22
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/_quality-gate-intro.hbs (renamed from server/sonar-web/src/main/coffee/apps/quality-gate/templates/_quality-gate-intro.hbs)0
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-actions.hbs (renamed from server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-actions.hbs)0
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-condition.hbs69
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-conditions-empty.hbs3
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-conditions.hbs39
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-header.hbs (renamed from server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-header.hbs)8
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-projects.hbs (renamed from server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-projects.hbs)4
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail.hbs (renamed from server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail.hbs)0
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-form.hbs20
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-condition-delete.hbs13
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-delete.hbs17
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-gate.hbs (renamed from server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-sidebar-list-item.hbs)2
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-gates.hbs1
-rw-r--r--server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-layout.hbs (renamed from server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gates-layout.hbs)0
-rw-r--r--server/sonar-web/src/main/js/components/common/handlebars-extensions.js2
-rw-r--r--server/sonar-web/src/main/less/init/forms.less7
-rw-r--r--server/sonar-web/src/main/less/init/icons.less4
-rw-r--r--server/sonar-web/src/main/less/init/misc.less1
-rw-r--r--server/sonar-web/src/main/less/init/tables.less2
-rw-r--r--server/sonar-web/src/main/webapp/WEB-INF/app/views/quality_gates/index.html.erb11
-rw-r--r--server/sonar-web/src/test/js/quality-gates-spec.js272
-rw-r--r--server/sonar-web/src/test/json/quality-gates-spec/projects.json10
-rw-r--r--server/sonar-web/src/test/views/quality_gates.jade2
69 files changed, 1308 insertions, 1384 deletions
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/app.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/app.coffee
deleted file mode 100644
index f238ddeb559..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/app.coffee
+++ /dev/null
@@ -1,118 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube 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.
-#
-# SonarQube 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.
-#
-define [
- './collections/quality-gates',
- './views/quality-gate-sidebar-list-view',
- './views/quality-gate-actions-view',
- './views/quality-gate-edit-view',
- './router',
- './layout'
-], (QualityGates,
- QualityGateSidebarListItemView,
- QualityGateActionsView,
- QualityGateEditView,
- QualityGateRouter,
- QualityGateLayout) ->
-
- # Create a Quality Gate Application
- App = new Marionette.Application
-
-
- App.qualityGates = new QualityGates
-
-
- App.openFirstQualityGate = ->
- App.layout.headerRegion.reset()
- App.layout.detailsRegion.reset()
-
-
- App.deleteQualityGate = (id) ->
- App.qualityGates.remove id
- App.openFirstQualityGate()
-
-
- App.unsetDefaults = (id) ->
- App.qualityGates.each (gate) ->
- gate.set('default', false) unless gate.id == id
-
-
- # Construct layout
- App.addInitializer ->
- @layout = new QualityGateLayout app: @
- jQuery('#quality-gates').append @layout.render().el
- jQuery('#footer').addClass 'search-navigator-footer'
-
-
- # Construct actions bar
- App.addInitializer ->
- @codingRulesHeaderView = new QualityGateActionsView
- app: @
- @layout.actionsRegion.show @codingRulesHeaderView
-
-
- # Construct sidebar
- App.addInitializer ->
- @qualityGateSidebarListView = new QualityGateSidebarListItemView
- collection: @qualityGates
- app: @
- @layout.resultsRegion.show @qualityGateSidebarListView
-
-
- # Construct edit view
- App.addInitializer ->
- @qualityGateEditView = new QualityGateEditView app: @
- @qualityGateEditView.render()
-
-
- # Start router
- App.addInitializer ->
- @router = new QualityGateRouter app: @
-
- QUALITY_GATES = '/quality_gates'
- path = window.location.pathname
- pos = path.indexOf QUALITY_GATES
- root = path.substr(0, pos + QUALITY_GATES.length)
- Backbone.history.start pushState: true, root: root
-
-
- # Open first quality gate when come to the page
- App.addInitializer ->
- initial = Backbone.history.fragment == ''
- App.openFirstQualityGate() if initial
-
-
- # Call app, Load metrics and the list of quality gates before start the application
- appXHR = jQuery.ajax
- url: "#{baseUrl}/api/qualitygates/app"
- .done (r) =>
- App.canEdit = r.edit
- App.periods = r.periods
- App.metrics = r.metrics
-
- qualityGatesXHR = App.qualityGates.fetch()
-
- # Message bundles
- l10nXHR = window.requestMessages()
-
- jQuery.when(qualityGatesXHR, appXHR, l10nXHR)
- .done ->
- # Start the application
- App.start()
-
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/collections/conditions.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/collections/conditions.coffee
deleted file mode 100644
index a3ecccc29d1..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/collections/conditions.coffee
+++ /dev/null
@@ -1,29 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube 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.
-#
-# SonarQube 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.
-#
-
-define [
- '../models/condition'
-], (
- Condition
-) ->
-
- class Conditions extends Backbone.Collection
- model: Condition
- comparator: 'metric'
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/collections/quality-gates.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/collections/quality-gates.coffee
deleted file mode 100644
index c74a50c4a97..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/collections/quality-gates.coffee
+++ /dev/null
@@ -1,48 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube 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.
-#
-# SonarQube 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.
-#
-
-define [
- '../models/quality-gate'
-], (
- QualityGate
-) ->
-
- class QualityGates extends Backbone.Collection
- model: QualityGate
-
-
- url: ->
- "#{baseUrl}/api/qualitygates/list"
-
-
- # {
- # "qualitygates": [
- # { "id": 42, "name": "QG 1" },
- # { "id": 43, "name": "QG 2" },
- # { "id": 44, "name": "QG 3" }
- # ],
- # "default": 42
- # }
- parse: (r) ->
- r.qualitygates.map (gate) ->
- _.extend gate, default: gate.id == r.default
-
-
- comparator: (item) -> item.get('name').toLowerCase()
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/layout.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/layout.coffee
deleted file mode 100644
index 36da66f1941..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/layout.coffee
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube 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.
-#
-# SonarQube 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.
-#
-
-define [
- './templates'
-], ->
-
- $ = jQuery
-
- class AppLayout extends Marionette.Layout
- template: Templates['quality-gates-layout']
-
-
- regions:
- headerRegion: '.search-navigator-workspace-header'
- actionsRegion: '.search-navigator-filters'
- resultsRegion: '.quality-gates-results'
- detailsRegion: '.search-navigator-workspace-details'
-
-
- onRender: ->
- $('.search-navigator').addClass 'sticky search-navigator-extended-view'
- top = $('.search-navigator').offset().top
- @$('.search-navigator-workspace-header').css top: top
- @$('.search-navigator-side').css({ top: top }).isolatedScroll()
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/models/condition.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/models/condition.coffee
deleted file mode 100644
index 7cf8ca03b82..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/models/condition.coffee
+++ /dev/null
@@ -1,58 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube 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.
-#
-# SonarQube 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.
-#
-
-define ->
-
- class Condition extends Backbone.Model
-
- url: ->
- "#{baseUrl}/api/qualitygates/create_condition"
-
-
- save: ->
- method = unless @isNew() then 'update' else 'create'
- data =
- metric: @get('metric').key
- op: @get('op')
- warning: @get('warning')
- error: @get('error')
-
- unless @get('period') == '0'
- data.period = @get('period')
-
- unless @isNew()
- data.id = @id
- else
- data.gateId = @get('gateId')
-
- jQuery.ajax({
- url: "#{baseUrl}/api/qualitygates/#{method}_condition"
- type: 'POST'
- data: data
- }).done (r) =>
- @set 'id', r.id
-
-
- delete: ->
- jQuery.ajax
- url: "#{baseUrl}/api/qualitygates/delete_condition"
- type: 'POST'
- data: id: @id
-
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/models/quality-gate.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/models/quality-gate.coffee
deleted file mode 100644
index 3b6dc6d1f41..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/models/quality-gate.coffee
+++ /dev/null
@@ -1,26 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube 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.
-#
-# SonarQube 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.
-#
-
-define ->
-
- class QualityGate extends Backbone.Model
-
- url: ->
- "#{baseUrl}/api/qualitygates/show?id=#{@get('id')}"
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/router.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/router.coffee
deleted file mode 100644
index 31fc35abe68..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/router.coffee
+++ /dev/null
@@ -1,62 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube 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.
-#
-# SonarQube 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.
-#
-
-define [
- './models/quality-gate',
- './views/quality-gate-detail-view',
- './views/quality-gate-detail-header-view',
-], (
- QualityGate,
- QualityGateDetailView,
- QualityGateDetailHeaderView
-) ->
-
- class QualityGateRouter extends Backbone.Router
-
- routes:
- '': 'index'
- 'show/:id': 'show'
-
-
- initialize: (options) ->
- @app = options.app
-
-
- index: ->
-
-
- show: (id) ->
- qualityGate = @app.qualityGates.get id
- if qualityGate
- @app.qualityGateSidebarListView.highlight id
-
- qualityGateDetailHeaderView = new QualityGateDetailHeaderView
- app: @app
- model: qualityGate
- @app.layout.headerRegion.show qualityGateDetailHeaderView
-
- qualityGateDetailView = new QualityGateDetailView
- app: @app
- model: qualityGate
- @app.layout.detailsRegion.show qualityGateDetailView
- qualityGateDetailView.$el.hide()
-
- qualityGate.fetch().done ->
- qualityGateDetailView.$el.show()
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-condition.hbs b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-condition.hbs
deleted file mode 100644
index 9be26807f78..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-condition.hbs
+++ /dev/null
@@ -1,62 +0,0 @@
-<td nowrap>
- {{metric.name}}
- {{#if metric.hidden}}
- <span class="deprecated">{{t 'deprecated'}}</span>
- {{/if}}
-</td>
-<td width="20%" nowrap>
- {{#if canEdit}}
- <select name="period" style="width: 100%">
- {{#unless isDiffMetric}}<option value="0">{{t 'value'}}</option>{{/unless}}
- {{#each periods}}<option value="{{key}}">&Delta; {{text}}</option>{{/each}}
- </select>
- {{else}}
- {{#if periodText}}&Delta; {{periodText}}
- {{else}}{{t 'value'}}
- {{/if}}
- {{/if}}
-</td>
-<td width="10%" nowrap>
- {{#if canEdit}}
- <select name="operator">
- {{#operators metric.type}}
- <option value="{{this}}">{{t 'quality_gates.operator' this}}</option>
- {{/operators}}
- </select>
- {{else}}
- {{t 'quality_gates.operator' op}}
- {{/if}}
-</td>
-<td width="1" class="nowrap">
- <i class="icon-alert-warn" title="{{t 'quality_gates.warning_tooltip'}}"></i>
- {{#if canEdit}}
- <input name="warning" class="input-small" data-type="{{metric.type}}" placeholder="{{metric.placeholder}}"
- type="text">
- {{else}}
- {{warning}}
- {{/if}}
-</td>
-<td width="1" class="nowrap">
- <i class="icon-alert-error" title="{{t 'quality_gates.error_tooltip'}}"></i>
- {{#if canEdit}}
- <input name="error" class="input-small" data-type="{{metric.type}}" placeholder="{{metric.placeholder}}"
- type="text">
- {{else}}
- {{error}}
- {{/if}}
-</td>
-<td class="quality-gate-condition-actions" width="120px" nowrap>
- {{#if canEdit}}
- {{#if id}}
- <div class="button-group">
- <button class="update-condition" disabled>{{t 'update_verb'}}</button>
- <button class="button-red delete-condition">{{t 'delete'}}</button>
- </div>
- {{else}}
- <div class="button-group">
- <button class="add-condition">{{t 'add_verb'}}</button>
- <a class="action cancel-add-condition">{{t 'cancel'}}</a>
- </div>
- {{/if}}
- {{/if}}
-</td>
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-conditions-empty.hbs b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-conditions-empty.hbs
deleted file mode 100644
index 40c92668496..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-conditions-empty.hbs
+++ /dev/null
@@ -1,3 +0,0 @@
-<td colspan="6">
- {{t 'quality_gates.no_conditions'}}
-</td> \ No newline at end of file
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-conditions.hbs b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-conditions.hbs
deleted file mode 100644
index 74888876431..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-conditions.hbs
+++ /dev/null
@@ -1,45 +0,0 @@
-<div class="quality-gate-section-name">{{t 'quality_gates.conditions'}}</div>
-
-<div class="quality-gate-introduction">
- <p>{{t 'quality_gates.introduction'}}
- <a class="link-action quality-gate-introduction-show-more">{{t 'more'}}</a>
- </p>
- <div class="quality-gate-introduction-more inline-help">
- {{t 'quality_gates.health_icons'}}
- <ul>
- <li>
- <i class="icon-alert-ok"></i>
- {{t 'alerts.notes.ok'}}
- </li>
- <li>
- <i class="icon-alert-warn"></i>
- {{t 'alerts.notes.warn'}}
- </li>
- <li>
- <i class="icon-alert-error"></i>
- {{t 'alerts.notes.error'}}
- </li>
- </ul>
- </div>
-</div>
-
-{{#if canEdit}}
- <div class="quality-gate-new-condition">
- <label for="quality-gate-new-condition-metric">{{t 'quality_gates.add_condition'}}:</label>
- <select id="quality-gate-new-condition-metric">
- <option></option>
- {{#each metricGroups}}
- <optgroup label="{{domain}}">
- {{#each metrics}}<option value="{{key}}">{{name}}</option>{{/each}}
- </optgroup>
- {{/each}}
- </select>
- </div>
-{{/if}}
-
-<div class="quality-gate-conditions-wrap">
- <table class="data zebra width100 marginbottom10 spaced quality-gate-conditions">
- <thead><tr></tr></thead>
- <tbody></tbody>
- </table>
-</div> \ No newline at end of file
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-edit.hbs b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-edit.hbs
deleted file mode 100644
index 5aeb891a816..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-edit.hbs
+++ /dev/null
@@ -1,22 +0,0 @@
-<form>
- <div class="modal-head">
- {{#if method}}
- <h2>{{t 'quality_gates' method }}</h2>
- {{/if}}
- </div>
-
- <div class="modal-body">
- <div class="alert alert-danger"></div>
- <div class="modal-field">
- <label for="quality-gate-edit-name">{{t 'name'}} <em class="mandatory">*</em></label>
- <input id="quality-gate-edit-name" type="text" size="50" maxlength="100" value="{{name}}">
- </div>
- </div>
-
- <div class="modal-foot">
- {{#eq method "rename"}}<button>{{t 'save'}}</button>{{/eq}}
- {{#eq method "copy"}}<button>{{t 'copy'}}</button>{{/eq}}
- {{#eq method "create"}}<button>{{t 'create'}}</button>{{/eq}}
- <a id="quality-gate-cancel-create" class="action">{{t 'cancel'}}</a>
- </div>
-</form>
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-sidebar-list-empty.hbs b/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-sidebar-list-empty.hbs
deleted file mode 100644
index 8b017f09987..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-sidebar-list-empty.hbs
+++ /dev/null
@@ -1 +0,0 @@
-<div class="line line-nowrap">{{t 'quality_gates.noQualityGates'}}</div> \ No newline at end of file
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-actions-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-actions-view.coffee
deleted file mode 100644
index 46e5f3e6d99..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-actions-view.coffee
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube 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.
-#
-# SonarQube 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.
-#
-
-define [
- '../models/quality-gate'
- '../templates'
-], (
- QualityGate
-) ->
-
- class QualityGateActionsView extends Marionette.ItemView
- template: Templates['quality-gate-actions']
-
-
- events:
- 'click #quality-gate-add': 'add'
-
-
- add: ->
- qualityGate = new QualityGate()
- @options.app.qualityGateEditView.method = 'create'
- @options.app.qualityGateEditView.model = qualityGate
- @options.app.qualityGateEditView.show()
-
-
- serializeData: ->
- _.extend super, canEdit: @options.app.canEdit
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-condition-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-condition-view.coffee
deleted file mode 100644
index 54c106126e2..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-condition-view.coffee
+++ /dev/null
@@ -1,146 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube 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.
-#
-# SonarQube 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.
-#
-
-define [
- '../templates'
-], ->
-
- class QualityGateDetailConditionView extends Marionette.ItemView
- tagName: 'tr'
- template: Templates['quality-gate-detail-condition']
- spinner: '<i class="spinner"></i>'
-
-
- modelEvents:
- 'change:id': 'render'
-
-
- ui:
- periodSelect: '[name=period]'
- operatorSelect: '[name=operator]'
- warningInput: '[name=warning]'
- errorInput: '[name=error]'
- actionsBox: '.quality-gate-condition-actions'
- updateButton: '.update-condition'
- deleteButton: '.delete-condition'
-
-
- events:
- 'click @ui.updateButton': 'saveCondition'
- 'click @ui.deleteButton': 'deleteCondition'
- 'click .add-condition': 'saveCondition'
- 'click .cancel-add-condition': 'cancelAddCondition'
- 'keyup :input': 'enableUpdate'
- 'change :input': 'enableUpdate'
-
-
- initialize: ->
- @populateMetric()
-
-
- populateMetric: ->
- metricKey = @model.get('metric')
- metric = _.findWhere @options.app.metrics, key: metricKey
- if metric?
- switch metric.type
- when 'WORK_DUR' then metric.placeholder = '1d 7h 59min'
- when 'RATING' then metric.placeholder = 'A'
- @model.set { metric: metric }, { silent: true }
- @model.set { isDiffMetric: metric.key.indexOf('new_') == 0 }, { silent: true }
-
-
- onRender: ->
- @ui.periodSelect.val @model.get('period') || '0'
- @ui.operatorSelect.val @model.get('op')
- @ui.warningInput.val @model.get('warning')
- @ui.errorInput.val @model.get('error')
-
- @ui.periodSelect.select2
- allowClear: false
- minimumResultsForSearch: 999
- width: '100%'
-
- @ui.operatorSelect.select2
- allowClear: false
- minimumResultsForSearch: 999
- width: '150px'
-
- @ui.periodSelect.select2('open') if @model.isNew()
-
-
- showSpinner: ->
- jQuery(@spinner).prependTo @ui.actionsBox
- @ui.actionsBox.find(':not(.spinner)').hide()
-
-
- hideSpinner: ->
- @ui.actionsBox.find('.spinner').remove()
- @ui.actionsBox.find(':not(.spinner)').show()
-
-
- saveCondition: ->
- @showSpinner()
- @model.set
- period: @ui.periodSelect.val()
- op: @ui.operatorSelect.val()
- warning: @ui.warningInput.val()
- error: @ui.errorInput.val()
- @model.save()
- .always =>
- @ui.updateButton.prop 'disabled', true
- @hideSpinner()
- .done =>
- @options.collectionView.updateConditions()
-
-
- deleteCondition: ->
- confirmDialog
- title: t 'quality_gates.delete_condition'
- html: tp 'quality_gates.delete_condition.confirm.message', @model.get('metric').name
- yesLabel: t 'delete'
- noLabel: t 'cancel'
- yesHandler: =>
- @showSpinner()
- @model.delete().done =>
- @options.collectionView.collection.remove @model
- @options.collectionView.updateConditions()
- @close()
- always: => @ui.deleteButton.blur()
-
-
- cancelAddCondition: ->
- @close()
-
-
- enableUpdate: ->
- @ui.updateButton.prop 'disabled', false
-
-
- serializeData: ->
- period = _.findWhere(@options.app.periods, key: this.model.get('period'))
- data = _.extend super,
- canEdit: @options.app.canEdit
- periods: @options.app.periods
- periodText: period?.text
- unless @options.app.canEdit
- _.extend data,
- warning: jQuery('<input>').data('type', @model.get('metric').type).val(@model.get('warning')).originalVal()
- error: jQuery('<input>').data('type', @model.get('metric').type).val(@model.get('error')).originalVal()
- data
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-conditions-empty-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-conditions-empty-view.coffee
deleted file mode 100644
index b285ca05617..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-conditions-empty-view.coffee
+++ /dev/null
@@ -1,27 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube 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.
-#
-# SonarQube 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.
-#
-
-define [
- '../templates'
-], ->
-
- class QualityGateDetailConditionsView extends Marionette.ItemView
- tagName: 'tr'
- template: Templates['quality-gate-detail-conditions-empty']
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-conditions-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-conditions-view.coffee
deleted file mode 100644
index 29843cbf32c..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-conditions-view.coffee
+++ /dev/null
@@ -1,105 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube 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.
-#
-# SonarQube 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.
-#
-
-define [
- '../models/condition',
- './quality-gate-detail-condition-view',
- './quality-gate-detail-conditions-empty-view'
- '../templates'
-], (
- Condition,
- QualityGateDetailConditionView,
- QualityGateDetailConditionsEmptyView,
-) ->
-
- class QualityGateDetailConditionsView extends Marionette.CompositeView
- template: Templates['quality-gate-detail-conditions']
- itemView: QualityGateDetailConditionView
- emptyView: QualityGateDetailConditionsEmptyView
- itemViewContainer: '.quality-gate-conditions tbody'
-
-
- ui:
- metricSelect: '#quality-gate-new-condition-metric'
- introductionShowMore: '.quality-gate-introduction-show-more'
- introductionMore: '.quality-gate-introduction-more'
-
-
- events:
- 'click @ui.introductionShowMore': 'showMoreIntroduction'
- 'change @ui.metricSelect': 'addCondition'
-
-
- itemViewOptions: ->
- app: @options.app
- collectionView: @
-
-
- appendHtml: (compositeView, itemView) ->
- if (compositeView.isBuffering)
- compositeView.elBuffer.appendChild itemView.el
- compositeView._bufferedChildren.push itemView
- else
- container = @getItemViewContainer compositeView
- container.prepend itemView.el
-
-
- onRender: ->
- @ui.introductionMore.hide()
- @ui.metricSelect.select2
- allowClear: false,
- width: '250px',
- placeholder: t('alerts.select_metric')
-
-
- groupedMetrics: ->
- metrics = @options.app.metrics
- metrics = _.filter metrics, (metric) ->
- !metric.hidden
- metrics = _.groupBy metrics, 'domain'
- metrics = _.map metrics, (metrics, domain) ->
- domain: domain, metrics: _.sortBy metrics, 'short_name'
- _.sortBy metrics, 'domain'
-
-
- serializeData: ->
- _.extend super,
- canEdit: @options.app.canEdit
- metricGroups: @groupedMetrics()
-
-
- showMoreIntroduction: ->
- @ui.introductionShowMore.hide()
- @ui.introductionMore.show()
-
-
- addCondition: ->
- metric = @ui.metricSelect.val()
- @ui.metricSelect.select2('val', '')
- condition = new Condition
- metric: metric
- gateId: @options.gateId
- @collection.unshift condition
-
-
- updateConditions: ->
- conditions = @collection.map (item) -> _.extend item.toJSON(),
- metric: item.get('metric').key
- @options.qualityGate.set { conditions: conditions }, { silent: true }
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-header-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-header-view.coffee
deleted file mode 100644
index 1980b9ed7c2..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-header-view.coffee
+++ /dev/null
@@ -1,101 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube 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.
-#
-# SonarQube 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.
-#
-
-define [
- '../models/quality-gate'
- '../templates',
-], (
- QualityGate
-) ->
-
- class QualityGateDetailHeaderView extends Marionette.ItemView
- template: Templates['quality-gate-detail-header']
- spinner: '<i class="spinner"></i>'
-
-
- modelEvents:
- 'change': 'render'
-
-
- ui:
- deleteButton: '#quality-gate-delete'
-
-
- events:
- 'click #quality-gate-rename': 'renameQualityGate'
- 'click #quality-gate-copy': 'copyQualityGate'
- 'click @ui.deleteButton': 'deleteQualityGate'
- 'click #quality-gate-set-as-default': 'setAsDefault'
- 'click #quality-gate-unset-as-default': 'unsetAsDefault'
-
-
- renameQualityGate: ->
- @options.app.qualityGateEditView.method = 'rename'
- @options.app.qualityGateEditView.model = @model
- @options.app.qualityGateEditView.show()
-
-
- copyQualityGate: ->
- copiedModel = new QualityGate @model.toJSON()
- copiedModel.set 'default', false
- @options.app.qualityGateEditView.method = 'copy'
- @options.app.qualityGateEditView.model = copiedModel
- @options.app.qualityGateEditView.show()
-
-
- deleteQualityGate: ->
- message = if @model.get 'default' then 'quality_gates.delete.confirm.default' else 'quality_gates.delete.confirm.message'
- message = tp message, @model.get('name')
- confirmDialog
- title: t 'quality_gates.delete'
- html: message
- yesLabel: t 'delete'
- noLabel: t 'cancel'
- yesHandler: =>
- jQuery.ajax
- type: 'POST'
- url: "#{baseUrl}/api/qualitygates/destroy"
- data: id: @model.id
- .done => @options.app.deleteQualityGate @model.id
- always: => @ui.deleteButton.blur()
-
-
- changeDefault: (set) ->
- data = if set then { id: @model.id } else {}
- method = if set then 'set_as_default' else 'unset_default'
- jQuery.ajax
- type: 'POST'
- url: "#{baseUrl}/api/qualitygates/#{method}"
- data: data
- .done =>
- @options.app.unsetDefaults @model.id
- @model.set 'default', !@model.get('default')
-
-
- setAsDefault: ->
- @changeDefault true
-
-
- unsetAsDefault: ->
- @changeDefault false
-
-
- serializeData: ->
- _.extend super, canEdit: @options.app.canEdit
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-projects-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-projects-view.coffee
deleted file mode 100644
index 726d95ac5c9..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-projects-view.coffee
+++ /dev/null
@@ -1,55 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube 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.
-#
-# SonarQube 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.
-#
-
-define [
- 'components/common/select-list'
- '../templates'
-], ->
-
- class QualityGateDetailProjectsView extends Marionette.ItemView
- template: Templates['quality-gate-detail-projects']
-
-
- onRender: ->
- unless @model.get('default')
- new SelectList
- el: @$('#select-list-projects')
- width: '100%'
- readOnly: !@options.app.canEdit
- focusSearch: false
- format: (item) -> item.name
- searchUrl: "#{baseUrl}/api/qualitygates/search?gateId=#{@options.gateId}"
- selectUrl: "#{baseUrl}/api/qualitygates/select"
- deselectUrl: "#{baseUrl}/api/qualitygates/deselect"
- extra:
- gateId: @options.gateId
- selectParameter: 'projectId'
- selectParameterValue: 'id'
- labels:
- selected: t('quality_gates.projects.with')
- deselected: t('quality_gates.projects.without')
- all: t('quality_gates.projects.all')
- noResults: t('quality_gates.projects.noResults')
- tooltips:
- select: t('quality_gates.projects.select_hint')
- deselect: t('quality_gates.projects.deselect_hint')
-
- serializeData: ->
- _.extend super, canEdit: @options.app.canEdit
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-view.coffee
deleted file mode 100644
index 10b4b6e0ceb..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-view.coffee
+++ /dev/null
@@ -1,67 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube 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.
-#
-# SonarQube 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.
-#
-
-define [
- '../collections/conditions',
- './quality-gate-detail-header-view',
- './quality-gate-detail-conditions-view',
- './quality-gate-detail-projects-view'
- '../templates',
-], (
- Conditions,
- QualityGateDetailHeaderView,
- QualityGateDetailConditionsView,
- QualityGateDetailProjectsView
-) ->
-
- class QualityGateDetailView extends Marionette.Layout
- template: Templates['quality-gate-detail']
-
-
- regions:
- conditionsRegion: '#quality-gate-conditions'
- projectsRegion: '#quality-gate-projects'
-
-
- modelEvents:
- 'change': 'render'
-
-
- onRender: ->
- @showConditions()
- @showProjects()
-
-
- showConditions: ->
- conditions = new Conditions @model.get('conditions')
- view = new QualityGateDetailConditionsView
- app: @options.app
- collection: conditions
- gateId: @model.id
- qualityGate: @model
- @conditionsRegion.show view
-
-
- showProjects: ->
- view = new QualityGateDetailProjectsView
- app: @options.app
- model: @model
- gateId: @model.id
- @projectsRegion.show view
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-edit-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-edit-view.coffee
deleted file mode 100644
index 67adfeca477..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-edit-view.coffee
+++ /dev/null
@@ -1,101 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube 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.
-#
-# SonarQube 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.
-#
-
-define [
- '../templates'
-], ->
-
- class QualityGateEditView extends Marionette.ItemView
- template: Templates['quality-gate-edit']
-
-
- ui:
- nameInput: '#quality-gate-edit-name'
-
-
- events:
- 'submit form': 'onSubmit'
- 'click #quality-gate-cancel-create': 'hide'
-
-
- onRender: ->
- @$el.dialog
- dialogClass: 'no-close',
- width: '600px',
- draggable: false,
- autoOpen: false,
- modal: true,
- minHeight: 50,
- resizable: false,
- title: null
-
-
- show: ->
- @render()
- @$el.dialog 'open'
- @ui.nameInput.focus()
-
-
- hide: ->
- @$el.dialog 'close'
-
-
- saveRequest: (data) ->
- jQuery.ajax
- type: 'POST'
- url: "#{baseUrl}/api/qualitygates/#{@method}"
- data: data
- .done => @hide()
-
-
- onSubmit: (e) ->
- e.preventDefault()
- switch @method
- when 'create' then @createQualityGate()
- when 'copy' then @copyQualityGate()
- when 'rename' then @saveQualityGate()
- else
-
-
- createQualityGate: ->
- data = name: @ui.nameInput.val()
- @saveRequest(data).done (r) =>
- @model.set id: r.id, name: r.name
- @options.app.qualityGates.add @model
- @options.app.router.navigate "show/#{r.id}", trigger: true
-
-
- saveQualityGate: ->
- data = id: @model.id, name: @ui.nameInput.val()
- @saveRequest(data).done (r) =>
- @model.set name: r.name
-
-
- copyQualityGate: ->
- data = id: @model.id, name: @ui.nameInput.val()
- @saveRequest(data).done (r) =>
- @model.set id: r.id, name: r.name
- @options.app.qualityGates.add @model
- @options.app.router.navigate "show/#{r.id}", trigger: true
-
-
- serializeData: ->
- if @model
- _.extend @model.toJSON(), method: @method
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-empty-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-empty-view.coffee
deleted file mode 100644
index 9ec9393dd9e..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-empty-view.coffee
+++ /dev/null
@@ -1,28 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube 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.
-#
-# SonarQube 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.
-#
-
-define [
- '../templates'
-], ->
-
- class QualityGateSidebarListEmptyView extends Marionette.ItemView
- tagName: 'li'
- className: 'empty'
- template: Templates['quality-gate-sidebar-list-empty']
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-item-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-item-view.coffee
deleted file mode 100644
index 3527a6c6e1a..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-item-view.coffee
+++ /dev/null
@@ -1,44 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube 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.
-#
-# SonarQube 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.
-#
-
-define [
- '../templates'
-], ->
-
- class QualityGateSidebarListItemView extends Marionette.ItemView
- tagName: 'a'
- className: 'list-group-item'
- template: Templates['quality-gate-sidebar-list-item']
-
-
- modelEvents:
- 'change': 'render'
-
-
- events:
- 'click': 'showQualityGate'
-
-
- onRender: ->
- @$el.toggleClass 'active', @options.highlighted
-
-
- showQualityGate: ->
- @options.app.router.navigate "show/#{@model.id}", trigger: true
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-view.coffee b/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-view.coffee
deleted file mode 100644
index 7fd40da3121..00000000000
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-view.coffee
+++ /dev/null
@@ -1,42 +0,0 @@
-#
-# SonarQube, open source software quality management tool.
-# Copyright (C) 2008-2014 SonarSource
-# mailto:contact AT sonarsource DOT com
-#
-# SonarQube 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.
-#
-# SonarQube 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.
-#
-
-define [
- './quality-gate-sidebar-list-item-view',
- './quality-gate-sidebar-list-empty-view'
-], (
- QualityGateSidebarListItemView,
- QualityGateSidebarListEmptyView,
-) ->
-
- class QualityGateSidebarListView extends Marionette.CollectionView
- className: 'list-group'
- itemView: QualityGateSidebarListItemView
- emptyView: QualityGateSidebarListEmptyView
-
-
- itemViewOptions: (model) ->
- app: @options.app
- highlighted: model.get('id') == +@highlighted
-
-
- highlight: (id) ->
- @highlighted = id
- @render()
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/actions-view.js b/server/sonar-web/src/main/js/apps/quality-gates/actions-view.js
new file mode 100644
index 00000000000..156c7d88104
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/actions-view.js
@@ -0,0 +1,27 @@
+define([
+ './create-view',
+ './templates'
+], function (CreateView) {
+
+ return Marionette.ItemView.extend({
+ template: Templates['quality-gate-actions'],
+
+ events: {
+ 'click #quality-gate-add': 'add'
+ },
+
+ add: function (e) {
+ e.preventDefault();
+ new CreateView({
+ collection: this.collection
+ }).render();
+ },
+
+ serializeData: function () {
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ canEdit: this.options.canEdit
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/app.js b/server/sonar-web/src/main/js/apps/quality-gates/app.js
new file mode 100644
index 00000000000..f5d521fe310
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/app.js
@@ -0,0 +1,69 @@
+define([
+ './gates',
+ './gates-view',
+ './actions-view',
+ './router',
+ './layout',
+ './controller'
+], function (Gates, GatesView, ActionsView, Router, Layout, Controller) {
+
+ var $ = jQuery,
+ App = new Marionette.Application();
+
+ var init = function (options) {
+ // Layout
+ this.layout = new Layout({ el: options.el });
+ this.layout.render();
+ $('#footer').addClass('search-navigator-footer');
+
+ // Gates List
+ this.gates = new Gates();
+
+ // Controller
+ this.controller = new Controller({ app: this });
+
+ // Header
+ this.actionsView = new ActionsView({
+ canEdit: this.canEdit,
+ collection: this.gates
+ });
+ this.layout.actionsRegion.show(this.actionsView);
+
+ // List
+ this.gatesView = new GatesView({
+ canEdit: this.canEdit,
+ collection: this.gates
+ });
+ this.layout.resultsRegion.show(this.gatesView);
+
+ // Router
+ this.router = new Router({ app: this });
+ Backbone.history.start({
+ pushState: true,
+ root: getRoot()
+ });
+ };
+
+ var appXHR = $.get(baseUrl + '/api/qualitygates/app')
+ .done(function (r) {
+ App.canEdit = r.edit;
+ App.periods = r.periods;
+ App.metrics = r.metrics;
+ });
+
+ App.on('start', function (options) {
+ $.when(window.requestMessages(), appXHR).done(function () {
+ init.call(App, options);
+ });
+ });
+
+ function getRoot () {
+ var ROOT = '/quality_gates',
+ path = window.location.pathname,
+ pos = path.indexOf(ROOT);
+ return path.substr(0, pos + ROOT.length);
+ }
+
+ return App;
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/condition.js b/server/sonar-web/src/main/js/apps/quality-gates/condition.js
new file mode 100644
index 00000000000..9ca451ee5a0
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/condition.js
@@ -0,0 +1,47 @@
+define(function () {
+
+ return Backbone.Model.extend({
+
+ defaults: {
+ period: 0
+ },
+
+ url: function () {
+ return baseUrl + '/api/qualitygates';
+ },
+
+ createUrl: function () {
+ return this.url() + '/create_condition';
+ },
+
+ updateUrl: function () {
+ return this.url() + '/update_condition';
+ },
+
+ deleteUrl: function () {
+ return this.url() + '/delete_condition';
+ },
+
+ sync: function (method, model, options) {
+ var opts = options || {};
+ opts.type = 'POST';
+ if (method === 'create') {
+ opts.url = this.createUrl();
+ opts.data = model.toJSON();
+ }
+ if (method === 'update') {
+ opts.url = this.updateUrl();
+ opts.data = model.toJSON();
+ }
+ if (method === 'delete') {
+ opts.url = this.deleteUrl();
+ opts.data = { id: model.id };
+ }
+ if (opts.data.period === '0') {
+ delete opts.data.period;
+ }
+ return Backbone.ajax(opts);
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/conditions.js b/server/sonar-web/src/main/js/apps/quality-gates/conditions.js
new file mode 100644
index 00000000000..49cbb23f069
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/conditions.js
@@ -0,0 +1,10 @@
+define([
+ './condition'
+], function (Condition) {
+
+ return Backbone.Collection.extend({
+ model: Condition,
+ comparator: 'metric'
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/controller.js b/server/sonar-web/src/main/js/apps/quality-gates/controller.js
new file mode 100644
index 00000000000..0b03a0a0d5c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/controller.js
@@ -0,0 +1,65 @@
+define([
+ './gate',
+ './details-view',
+ './header-view'
+], function (Gate, DetailsView, HeaderView) {
+
+ return Marionette.Controller.extend({
+
+ initialize: function (options) {
+ this.app = options.app;
+ this.canEdit = this.app.canEdit;
+ this.listenTo(this.app.gates, 'select', this.onSelect);
+ this.listenTo(this.app.gates, 'destroy', this.onDestroy);
+ },
+
+ index: function () {
+ this.app.gates.fetch();
+ },
+
+ show: function (id) {
+ var that = this;
+ this.app.gates.fetch().done(function () {
+ var gate = that.app.gates.get(id);
+ if (gate != null) {
+ gate.trigger('select', gate, { trigger: false });
+ }
+ });
+ },
+
+ onSelect: function (gate, options) {
+ var that = this,
+ route = 'show/' + gate.id,
+ opts = _.defaults(options || {}, { trigger: true });
+ if (opts.trigger) {
+ this.app.router.navigate(route);
+ }
+ this.app.gatesView.highlight(gate.id);
+ gate.fetch().done(function () {
+ var headerView = new HeaderView({
+ model: gate,
+ canEdit: that.canEdit
+ });
+ that.app.layout.headerRegion.show(headerView);
+
+ var detailsView = new DetailsView({
+ model: gate,
+ canEdit: that.canEdit,
+ metrics: that.app.metrics,
+ periods: that.app.periods
+ });
+ that.app.layout.detailsRegion.show(detailsView);
+ });
+ },
+
+ onDestroy: function () {
+ this.app.router.navigate('');
+ this.app.layout.headerRegion.reset();
+ this.app.layout.detailsRegion.reset();
+ //this.app.layout.renderIntro();
+ this.app.gatesView.highlight(null);
+ }
+
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/copy-view.js b/server/sonar-web/src/main/js/apps/quality-gates/copy-view.js
new file mode 100644
index 00000000000..fdd6c974cf8
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/copy-view.js
@@ -0,0 +1,24 @@
+define([
+ './form-view'
+], function (FormView) {
+
+ return FormView.extend({
+ method: 'copy',
+
+ prepareRequest: function () {
+ var that = this;
+ var url = baseUrl + '/api/qualitygates/copy',
+ name = this.$('#quality-gate-form-name').val(),
+ options = {
+ url: url,
+ data: { id: this.model.id, name: name }
+ };
+ return this.sendRequest(options)
+ .done(function (r) {
+ var gate = that.addGate(r);
+ gate.trigger('select', gate);
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/create-view.js b/server/sonar-web/src/main/js/apps/quality-gates/create-view.js
new file mode 100644
index 00000000000..f58863ba315
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/create-view.js
@@ -0,0 +1,24 @@
+define([
+ './form-view'
+], function (FormView) {
+
+ return FormView.extend({
+ method: 'create',
+
+ prepareRequest: function () {
+ var that = this;
+ var url = baseUrl + '/api/qualitygates/create',
+ name = this.$('#quality-gate-form-name').val(),
+ options = {
+ url: url,
+ data: { name: name }
+ };
+ return this.sendRequest(options)
+ .done(function (r) {
+ var gate = that.addGate(r);
+ gate.trigger('select', gate);
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/delete-view.js b/server/sonar-web/src/main/js/apps/quality-gates/delete-view.js
new file mode 100644
index 00000000000..793934460ca
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/delete-view.js
@@ -0,0 +1,33 @@
+define([
+ 'components/common/modal-form',
+ './templates'
+], function (ModalForm) {
+
+ return ModalForm.extend({
+ template: Templates['quality-gates-delete'],
+
+ onFormSubmit: function () {
+ ModalForm.prototype.onFormSubmit.apply(this, arguments);
+ this.disableForm();
+ this.sendRequest();
+ },
+
+ sendRequest: function () {
+ var that = this,
+ options = {
+ statusCode: {
+ // do not show global error
+ 400: null
+ }
+ };
+ return this.model.destroy(options)
+ .done(function () {
+ that.close();
+ }).fail(function (jqXHR) {
+ that.enableForm();
+ that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings);
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/details-view.js b/server/sonar-web/src/main/js/apps/quality-gates/details-view.js
new file mode 100644
index 00000000000..df054717447
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/details-view.js
@@ -0,0 +1,46 @@
+define([
+ './conditions',
+ './gate-conditions-view',
+ './gate-projects-view',
+ './templates'
+], function (Conditions, DetailConditionsView, ProjectsView) {
+
+ return Marionette.Layout.extend({
+ template: Templates['quality-gate-detail'],
+
+ regions: {
+ conditionsRegion: '#quality-gate-conditions',
+ projectsRegion: '#quality-gate-projects'
+ },
+
+ modelEvents: {
+ 'change': 'render'
+ },
+
+ onRender: function () {
+ this.showConditions();
+ this.showProjects();
+ },
+
+ showConditions: function () {
+ var conditions = new Conditions(this.model.get('conditions')),
+ view = new DetailConditionsView({
+ canEdit: this.options.canEdit,
+ collection: conditions,
+ model: this.model,
+ metrics: this.options.metrics,
+ periods: this.options.periods
+ });
+ this.conditionsRegion.show(view);
+ },
+
+ showProjects: function () {
+ var view = new ProjectsView({
+ canEdit: this.options.canEdit,
+ model: this.model
+ });
+ this.projectsRegion.show(view);
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/form-view.js b/server/sonar-web/src/main/js/apps/quality-gates/form-view.js
new file mode 100644
index 00000000000..5da9dfd05a2
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/form-view.js
@@ -0,0 +1,47 @@
+define([
+ 'components/common/modal-form',
+ './gate',
+ './templates'
+], function (ModalForm, Gate) {
+
+ return ModalForm.extend({
+ template: Templates['quality-gate-form'],
+
+ onFormSubmit: function () {
+ ModalForm.prototype.onFormSubmit.apply(this, arguments);
+ this.disableForm();
+ this.prepareRequest();
+ },
+
+ sendRequest: function (options) {
+ var that = this,
+ opts = _.defaults(options || {}, {
+ type: 'POST',
+ statusCode: {
+ // do not show global error
+ 400: null
+ }
+ });
+ return Backbone.ajax(opts)
+ .done(function () {
+ that.close();
+ }).fail(function (jqXHR) {
+ that.enableForm();
+ that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings);
+ });
+ },
+
+ addGate: function (attrs) {
+ var gate = new Gate(attrs);
+ this.collection.add(gate, { merge: true });
+ return gate;
+ },
+
+ serializeData: function () {
+ return _.extend(ModalForm.prototype.serializeData.apply(this, arguments), {
+ method: this.method
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/gate-condition-view.js b/server/sonar-web/src/main/js/apps/quality-gates/gate-condition-view.js
new file mode 100644
index 00000000000..49797716adf
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/gate-condition-view.js
@@ -0,0 +1,100 @@
+define([
+ './gate-conditions-delete-view',
+ './templates'
+], function (DeleteConditionView) {
+
+ return Marionette.ItemView.extend({
+ tagName: 'tr',
+ template: Templates['quality-gate-detail-condition'],
+
+ modelEvents: {
+ 'change': 'render'
+ },
+
+ ui: {
+ periodSelect: '[name=period]',
+ operatorSelect: '[name=operator]',
+ warningInput: '[name=warning]',
+ errorInput: '[name=error]',
+ actionsBox: '.quality-gate-condition-actions',
+ updateButton: '.update-condition',
+ deleteButton: '.delete-condition'
+ },
+
+ events: {
+ 'click @ui.updateButton': 'saveCondition',
+ 'click @ui.deleteButton': 'deleteCondition',
+ 'click .add-condition': 'saveCondition',
+ 'click .cancel-add-condition': 'cancelAddCondition',
+ 'keyup :input': 'enableUpdate',
+ 'change :input': 'enableUpdate'
+ },
+
+ onRender: function () {
+ this.ui.warningInput.val(this.model.get('warning'));
+ this.ui.errorInput.val(this.model.get('error'));
+
+ this.ui.periodSelect.select2({
+ allowClear: false,
+ minimumResultsForSearch: 999
+ });
+
+ this.ui.operatorSelect.select2({
+ allowClear: false,
+ minimumResultsForSearch: 999
+ });
+
+ if (this.model.isNew()) {
+ this.ui.periodSelect.select2('open');
+ }
+ },
+
+ saveCondition: function () {
+ var attrs = {
+ gateId: this.model.isNew() ? this.options.gate.id : void 0,
+ period: this.ui.periodSelect.val(),
+ op: this.ui.operatorSelect.val(),
+ warning: this.ui.warningInput.val(),
+ error: this.ui.errorInput.val()
+ };
+ this.model.save(attrs, { wait: true });
+ },
+
+ deleteCondition: function () {
+ new DeleteConditionView({
+ model: this.model,
+ metric: this.getMetric()
+ }).render();
+ },
+
+ cancelAddCondition: function () {
+ this.close();
+ },
+
+ enableUpdate: function () {
+ this.ui.updateButton.prop('disabled', false);
+ },
+
+ getMetric: function () {
+ var key = this.model.get('metric');
+ return _.findWhere(this.options.metrics, { key: key });
+ },
+
+ isDiffMetric: function () {
+ var key = this.model.get('metric');
+ return key.indexOf('new_') === 0;
+ },
+
+ serializeData: function () {
+ var period = _.findWhere(this.options.periods, { key: this.model.get('period') });
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ canEdit: this.options.canEdit,
+ periods: this.options.periods,
+ periodText: period ? period.text : t('value'),
+ metric: this.getMetric(),
+ isDiffMetric: this.isDiffMetric()
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-delete-view.js b/server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-delete-view.js
new file mode 100644
index 00000000000..83e32ee2455
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-delete-view.js
@@ -0,0 +1,39 @@
+define([
+ 'components/common/modal-form',
+ './templates'
+], function (ModalForm) {
+
+ return ModalForm.extend({
+ template: Templates['quality-gates-condition-delete'],
+
+ onFormSubmit: function () {
+ ModalForm.prototype.onFormSubmit.apply(this, arguments);
+ this.disableForm();
+ this.sendRequest();
+ },
+
+ sendRequest: function () {
+ var that = this,
+ options = {
+ statusCode: {
+ // do not show global error
+ 400: null
+ }
+ };
+ return this.model.destroy(options)
+ .done(function () {
+ that.close();
+ }).fail(function (jqXHR) {
+ that.enableForm();
+ that.showErrors(jqXHR.responseJSON.errors, jqXHR.responseJSON.warnings);
+ });
+ },
+
+ serializeData: function () {
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ metric: this.options.metric
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-empty-view.js b/server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-empty-view.js
new file mode 100644
index 00000000000..03390ac7a89
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-empty-view.js
@@ -0,0 +1,16 @@
+define([
+ './templates'
+], function () {
+
+ return Marionette.ItemView.extend({
+ tagName: 'tr',
+ template: Templates['quality-gate-detail-conditions-empty'],
+
+ serializeData: function () {
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ canEdit: this.options.canEdit
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-view.js b/server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-view.js
new file mode 100644
index 00000000000..5c8353b7e00
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-view.js
@@ -0,0 +1,75 @@
+define([
+ './condition',
+ './gate-condition-view',
+ './gate-conditions-empty-view',
+ './templates'
+], function (Condition, ConditionView, ConditionsEmptyView) {
+
+ return Marionette.CompositeView.extend({
+ template: Templates['quality-gate-detail-conditions'],
+ itemView: ConditionView,
+ emptyView: ConditionsEmptyView,
+ itemViewContainer: '.js-conditions',
+
+ ui: {
+ metricSelect: '#quality-gate-new-condition-metric'
+ },
+
+ events: {
+ 'click .js-show-more': 'showMoreIntroduction',
+ 'change @ui.metricSelect': 'addCondition'
+ },
+
+ itemViewOptions: function () {
+ return {
+ canEdit: this.options.canEdit,
+ gate: this.model,
+ collectionView: this,
+ metrics: this.options.metrics,
+ periods: this.options.periods
+ };
+ },
+
+ onRender: function () {
+ this.ui.metricSelect.select2({
+ allowClear: false,
+ width: '250px',
+ placeholder: t('alerts.select_metric')
+ });
+ },
+
+ showMoreIntroduction: function () {
+ this.$('.js-show-more').addClass('hidden');
+ this.$('.js-more').removeClass('hidden');
+ },
+
+ addCondition: function () {
+ var metric = this.ui.metricSelect.val();
+ this.ui.metricSelect.select2('val', '');
+ var condition = new Condition({ metric: metric });
+ this.collection.add(condition);
+ },
+
+ groupedMetrics: function () {
+ var metrics = this.options.metrics.filter(function (metric) {
+ return !metric.hidden;
+ });
+ metrics = _.groupBy(metrics, 'domain');
+ metrics = _.map(metrics, function (metrics, domain) {
+ return {
+ domain: domain,
+ metrics: _.sortBy(metrics, 'short_name')
+ };
+ });
+ return _.sortBy(metrics, 'domain');
+ },
+
+ serializeData: function () {
+ return _.extend(Marionette.CompositeView.prototype.serializeData.apply(this, arguments), {
+ canEdit: this.options.canEdit,
+ metricGroups: this.groupedMetrics()
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/gate-projects-view.js b/server/sonar-web/src/main/js/apps/quality-gates/gate-projects-view.js
new file mode 100644
index 00000000000..c188c473be5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/gate-projects-view.js
@@ -0,0 +1,48 @@
+define([
+ 'components/common/select-list',
+ './templates'
+], function () {
+
+ return Marionette.ItemView.extend({
+ template: Templates['quality-gate-detail-projects'],
+
+ onRender: function () {
+ if (!this.model.isDefault()) {
+ new window.SelectList({
+ el: this.$('#select-list-projects'),
+ width: '100%',
+ readOnly: !this.options.canEdit,
+ focusSearch: false,
+ format: function (item) {
+ return item.name;
+ },
+ searchUrl: baseUrl + '/api/qualitygates/search?gateId=' + this.model.id,
+ selectUrl: baseUrl + '/api/qualitygates/select',
+ deselectUrl: baseUrl + '/api/qualitygates/deselect',
+ extra: {
+ gateId: this.model.id
+ },
+ selectParameter: 'projectId',
+ selectParameterValue: 'id',
+ labels: {
+ selected: t('quality_gates.projects.with'),
+ deselected: t('quality_gates.projects.without'),
+ all: t('quality_gates.projects.all'),
+ noResults: t('quality_gates.projects.noResults')
+ },
+ tooltips: {
+ select: t('quality_gates.projects.select_hint'),
+ deselect: t('quality_gates.projects.deselect_hint')
+ }
+ });
+ }
+ },
+
+ serializeData: function () {
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ canEdit: this.options.canEdit
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/gate-view.js b/server/sonar-web/src/main/js/apps/quality-gates/gate-view.js
new file mode 100644
index 00000000000..30036a3c313
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/gate-view.js
@@ -0,0 +1,29 @@
+define([
+ './templates'
+], function () {
+
+ return Marionette.ItemView.extend({
+ tagName: 'a',
+ className: 'list-group-item',
+ template: Templates['quality-gates-gate'],
+
+ modelEvents: {
+ 'change': 'render'
+ },
+
+ events: {
+ 'click': 'onClick'
+ },
+
+ onRender: function () {
+ this.$el.toggleClass('active', this.options.highlighted);
+ this.$el.attr('data-id', this.model.id);
+ },
+
+ onClick: function (e) {
+ e.preventDefault();
+ this.model.trigger('select', this.model);
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/gate.js b/server/sonar-web/src/main/js/apps/quality-gates/gate.js
new file mode 100644
index 00000000000..54b4e6ce13c
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/gate.js
@@ -0,0 +1,54 @@
+define(function () {
+
+ return Backbone.Model.extend({
+
+ isDefault: function () {
+ return this.get('isDefault');
+ },
+
+ url: function () {
+ return baseUrl + '/api/qualitygates';
+ },
+
+ showUrl: function () {
+ return this.url() + '/show';
+ },
+
+ deleteUrl: function () {
+ return this.url() + '/destroy';
+ },
+
+ toggleDefaultUrl: function () {
+ var method = this.isDefault() ? 'unset_default' : 'set_as_default';
+ return this.url() + '/' + method;
+ },
+
+ sync: function (method, model, options) {
+ var opts = options || {};
+ opts.data = opts.data || {};
+ opts.data.id = model.id;
+ if (method === 'read') {
+ opts.url = this.showUrl();
+ }
+ if (method === 'delete') {
+ opts.url = this.deleteUrl();
+ opts.type = 'POST';
+ }
+ return Backbone.ajax(opts);
+ },
+
+ toggleDefault: function () {
+ var that = this;
+ var opts = {
+ type: 'POST',
+ url: this.toggleDefaultUrl(),
+ data: { id: this.id }
+ };
+ return Backbone.ajax(opts).done(function () {
+ that.collection.toggleDefault(that);
+ });
+ }
+
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/gates-view.js b/server/sonar-web/src/main/js/apps/quality-gates/gates-view.js
new file mode 100644
index 00000000000..2fa1ceb6d18
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/gates-view.js
@@ -0,0 +1,25 @@
+define([
+ './gate-view',
+ './templates'
+], function (ItemView) {
+
+ return Marionette.CompositeView.extend({
+ className: 'list-group',
+ template: Templates['quality-gates-gates'],
+ itemView: ItemView,
+ itemViewContainer: '.js-list',
+
+ itemViewOptions: function (model) {
+ return {
+ collectionView: this,
+ highlighted: model.id === this.highlighted
+ };
+ },
+
+ highlight: function (id) {
+ this.highlighted = id;
+ this.render();
+ },
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/gates.js b/server/sonar-web/src/main/js/apps/quality-gates/gates.js
new file mode 100644
index 00000000000..d33b35bcfe5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/gates.js
@@ -0,0 +1,30 @@
+define([
+ './gate'
+], function (Gate) {
+
+ return Backbone.Collection.extend({
+ model: Gate,
+
+ url: function () {
+ return baseUrl + '/api/qualitygates/list';
+ },
+
+ parse: function (r) {
+ return r.qualitygates.map(function (gate) {
+ return _.extend(gate, { isDefault: gate.id === r.default });
+ });
+ },
+
+ comparator: function (item) {
+ return item.get('name').toLowerCase();
+ },
+
+ toggleDefault: function (gate) {
+ var isDefault = gate.isDefault();
+ this.forEach(function (model) {
+ model.set({ isDefault: gate.id === model.id ? !isDefault : false });
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/header-view.js b/server/sonar-web/src/main/js/apps/quality-gates/header-view.js
new file mode 100644
index 00000000000..b86b2c9ba62
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/header-view.js
@@ -0,0 +1,52 @@
+define([
+ './rename-view',
+ './copy-view',
+ './delete-view',
+ './templates'
+], function (RenameView, CopyView, DeleteView) {
+
+ return Marionette.ItemView.extend({
+ template: Templates['quality-gate-detail-header'],
+
+ modelEvents: {
+ 'change': 'render'
+ },
+
+ events: {
+ 'click #quality-gate-rename': 'renameQualityGate',
+ 'click #quality-gate-copy': 'copyQualityGate',
+ 'click #quality-gate-delete': 'deleteQualityGate',
+ 'click #quality-gate-toggle-default': 'toggleDefault'
+ },
+
+ renameQualityGate: function () {
+ new RenameView({
+ model: this.model
+ }).render();
+ },
+
+ copyQualityGate: function () {
+ new CopyView({
+ model: this.model,
+ collection: this.model.collection
+ }).render();
+ },
+
+ deleteQualityGate: function () {
+ new DeleteView({
+ model: this.model
+ }).render();
+ },
+
+ toggleDefault: function () {
+ this.model.toggleDefault();
+ },
+
+ serializeData: function () {
+ return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
+ canEdit: this.options.canEdit
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/layout.js b/server/sonar-web/src/main/js/apps/quality-gates/layout.js
new file mode 100644
index 00000000000..8d695fd34bc
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/layout.js
@@ -0,0 +1,24 @@
+define([
+ './templates'
+], function () {
+
+ var $ = jQuery;
+
+ return Marionette.Layout.extend({
+ template: Templates['quality-gates-layout'],
+
+ regions: {
+ headerRegion: '.search-navigator-workspace-header',
+ actionsRegion: '.search-navigator-filters',
+ resultsRegion: '.quality-gates-results',
+ detailsRegion: '.search-navigator-workspace-details'
+ },
+
+ onRender: function () {
+ var top = $('.search-navigator').offset().top;
+ this.$('.search-navigator-workspace-header').css({ top: top });
+ this.$('.search-navigator-side').css({ top: top }).isolatedScroll();
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/rename-view.js b/server/sonar-web/src/main/js/apps/quality-gates/rename-view.js
new file mode 100644
index 00000000000..d9b5946653a
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/rename-view.js
@@ -0,0 +1,23 @@
+define([
+ './form-view'
+], function (FormView) {
+
+ return FormView.extend({
+ method: 'rename',
+
+ prepareRequest: function () {
+ var that = this;
+ var url = baseUrl + '/api/qualitygates/rename',
+ name = this.$('#quality-gate-form-name').val(),
+ options = {
+ url: url,
+ data: { id: this.model.id, name: name }
+ };
+ return this.sendRequest(options)
+ .done(function (r) {
+ that.model.set(r);
+ });
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/router.js b/server/sonar-web/src/main/js/apps/quality-gates/router.js
new file mode 100644
index 00000000000..582c47361d5
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/router.js
@@ -0,0 +1,22 @@
+define(function () {
+
+ return Backbone.Router.extend({
+ routes: {
+ '': 'index',
+ 'show/:id': 'show'
+ },
+
+ initialize: function (options) {
+ this.app = options.app;
+ },
+
+ index: function () {
+ this.app.controller.index();
+ },
+
+ show: function (id) {
+ this.app.controller.show(id);
+ }
+ });
+
+});
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/_quality-gate-intro.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/_quality-gate-intro.hbs
index 6f194fb7fbb..6f194fb7fbb 100644
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/_quality-gate-intro.hbs
+++ b/server/sonar-web/src/main/js/apps/quality-gates/templates/_quality-gate-intro.hbs
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-actions.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-actions.hbs
index 6826aa94353..6826aa94353 100644
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-actions.hbs
+++ b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-actions.hbs
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-condition.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-condition.hbs
new file mode 100644
index 00000000000..72a50887e1f
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-condition.hbs
@@ -0,0 +1,69 @@
+<td class="text-middle nowrap">
+ {{metric.name}}
+ {{#if metric.hidden}}
+ <span class="text-danger little-spacer-left">{{t 'deprecated'}}</span>
+ {{/if}}
+</td>
+
+<td class="thin text-middle nowrap">
+ {{#if canEdit}}
+ <select class="input-medium" name="period">
+ {{#unless isDiffMetric}}
+ <option value="0" {{#eq period 0}}selected{{/eq}}>{{t 'value'}}</option>
+ {{/unless}}
+ {{#each periods}}
+ <option value="{{key}}" {{#eq key ../period}}selected{{/eq}}>&Delta; {{text}}</option>
+ {{/each}}
+ </select>
+ {{else}}
+ &Delta; {{periodText}}
+ {{/if}}
+</td>
+
+<td class="thin text-middle nowrap">
+ {{#if canEdit}}
+ <select class="input-small" name="operator">
+ {{#operators}}
+ <option value="{{this}}" {{#eq this ../op}}selected{{/eq}}>{{t 'quality_gates.operator' this}}</option>
+ {{/operators}}
+ </select>
+ {{else}}
+ {{t 'quality_gates.operator' op}}
+ {{/if}}
+</td>
+
+<td class="thin text-middle nowrap">
+ <i class="icon-alert-warn" title="{{t 'quality_gates.warning_tooltip'}}"></i>
+ {{#if canEdit}}
+ <input name="warning" type="text" class="input-tiny text-middle" data-type="{{metric.type}}"
+ placeholder="{{metric.placeholder}}">
+ {{else}}
+ {{formatMeasure warning metric.type}}
+ {{/if}}
+</td>
+
+<td class="thin text-middle nowrap">
+ <i class="icon-alert-error" title="{{t 'quality_gates.error_tooltip'}}"></i>
+ {{#if canEdit}}
+ <input name="error" type="text" class="input-tiny text-middle" data-type="{{metric.type}}"
+ placeholder="{{metric.placeholder}}">
+ {{else}}
+ {{formatMeasure error metric.type}}
+ {{/if}}
+</td>
+
+{{#if canEdit}}
+ <td class="thin text-middle nowrap">
+ {{#if id}}
+ <div class="button-group">
+ <button class="update-condition" disabled>{{t 'update_verb'}}</button>
+ <button class="button-red delete-condition">{{t 'delete'}}</button>
+ </div>
+ {{else}}
+ <div class="button-group">
+ <button class="add-condition">{{t 'add_verb'}}</button>
+ <a class="action cancel-add-condition">{{t 'cancel'}}</a>
+ </div>
+ {{/if}}
+ </td>
+{{/if}}
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-conditions-empty.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-conditions-empty.hbs
new file mode 100644
index 00000000000..80a12b45883
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-conditions-empty.hbs
@@ -0,0 +1,3 @@
+<td colspan="{{#if canEdit}}6{{else}}5{{/if}}">
+ {{t 'quality_gates.no_conditions'}}
+</td>
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-conditions.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-conditions.hbs
new file mode 100644
index 00000000000..350aa94086d
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-conditions.hbs
@@ -0,0 +1,39 @@
+<h3 class="spacer-bottom">{{t 'quality_gates.conditions'}}</h3>
+
+<div class="alert alert-info">
+ <div>
+ {{t 'quality_gates.introduction'}}
+ <a class="js-show-more pull-right spacer-left">{{t 'more'}}&nbsp;<i class="icon-dropdown"></i></a>
+ </div>
+ <div class="js-more spacer-top hidden">
+ {{t 'quality_gates.health_icons'}}
+ <ul>
+ <li class="little-spacer-top">
+ <i class="icon-alert-ok"></i> {{t 'alerts.notes.ok'}}
+ </li>
+ <li class="little-spacer-top">
+ <i class="icon-alert-warn"></i> {{t 'alerts.notes.warn'}}
+ </li>
+ <li class="little-spacer-top">
+ <i class="icon-alert-error"></i> {{t 'alerts.notes.error'}}
+ </li>
+ </ul>
+ </div>
+</div>
+
+{{#if canEdit}}
+ <form class="big-spacer-top spacer-bottom">
+ <label for="quality-gate-new-condition-metric" class="text-middle">{{t 'quality_gates.add_condition'}}</label>
+ <select id="quality-gate-new-condition-metric" class="text-middle">
+ <option></option>
+ {{#each metricGroups}}
+ <optgroup label="{{domain}}">
+ {{#each metrics}}
+ <option value="{{key}}">{{name}}</option>{{/each}}
+ </optgroup>
+ {{/each}}
+ </select>
+ </form>
+{{/if}}
+
+<table class="js-conditions data zebra width-100"></table>
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-header.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-header.hbs
index 71f7e3b06de..c7fcf9a84d0 100644
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-header.hbs
+++ b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-header.hbs
@@ -5,11 +5,9 @@
<div class="button-group">
<button id="quality-gate-rename">{{t 'rename'}}</button>
<button id="quality-gate-copy">{{t 'copy'}}</button>
- {{#if default}}
- <button id="quality-gate-unset-as-default">{{t 'unset_as_default'}}</button>
- {{else}}
- <button id="quality-gate-set-as-default">{{t 'set_as_default'}}</button>
- {{/if}}
+ <button id="quality-gate-toggle-default">
+ {{#if isDefault}}{{t 'unset_as_default'}}{{else}}{{t 'set_as_default'}}{{/if}}
+ </button>
<button id="quality-gate-delete" class="button-red">{{t 'delete'}}</button>
</div>
</div>
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-projects.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-projects.hbs
index 989cae0f31e..1622542fcc5 100644
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-projects.hbs
+++ b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-projects.hbs
@@ -1,6 +1,6 @@
<div class="quality-gate-section-name">{{t 'quality_gates.projects'}}</div>
-{{#if default}}
+{{#if isDefault}}
<p class="quality-gate-default-message">
{{#if canEdit}}
{{t 'quality_gates.projects_for_default.edit'}}
@@ -10,4 +10,4 @@
</p>
{{else}}
<div id="select-list-projects"></div>
-{{/if}} \ No newline at end of file
+{{/if}}
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail.hbs
index 0c5467b3476..0c5467b3476 100644
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail.hbs
+++ b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail.hbs
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-form.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-form.hbs
new file mode 100644
index 00000000000..bc6e623018e
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-form.hbs
@@ -0,0 +1,20 @@
+<form id="quality-gate-form">
+ <div class="modal-head">
+ <h2>{{t 'quality_gates' method }}</h2>
+ </div>
+ <div class="modal-body">
+ <div class="js-modal-messages"></div>
+ <div class="modal-field">
+ <label for="quality-gate-form-name">{{t 'name'}} <em class="mandatory">*</em></label>
+ <input id="quality-gate-form-name" type="text" size="50" maxlength="100" value="{{name}}" required>
+ </div>
+ </div>
+ <div class="modal-foot">
+ <button id="quality-gate-form-submit">
+ {{#eq method 'rename'}}{{t 'save'}}{{/eq}}
+ {{#eq method 'copy'}}{{t 'copy'}}{{/eq}}
+ {{#eq method 'create'}}{{t 'create'}}{{/eq}}
+ </button>
+ <a id="quality-gate-form-cancel" href="#" class="js-modal-close">{{t 'cancel'}}</a>
+ </div>
+</form>
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-condition-delete.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-condition-delete.hbs
new file mode 100644
index 00000000000..221d0380016
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-condition-delete.hbs
@@ -0,0 +1,13 @@
+<form id="delete-condition-form">
+ <div class="modal-head">
+ <h2>{{t 'quality_gates.delete_condition'}}</h2>
+ </div>
+ <div class="modal-body">
+ <div class="js-modal-messages"></div>
+ {{tp 'quality_gates.delete_condition.confirm.message' metric.name}}
+ </div>
+ <div class="modal-foot">
+ <button id="delete-condition-submit">{{t 'delete'}}</button>
+ <a href="#" class="js-modal-close" id="delete-condition-cancel">{{t 'cancel'}}</a>
+ </div>
+</form>
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-delete.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-delete.hbs
new file mode 100644
index 00000000000..c146c581bae
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-delete.hbs
@@ -0,0 +1,17 @@
+<form id="delete-gate-form">
+ <div class="modal-head">
+ <h2>{{t 'quality_gates.delete'}}</h2>
+ </div>
+ <div class="modal-body">
+ <div class="js-modal-messages"></div>
+ {{#if isDefault}}
+ {{tp 'quality_gates.delete.confirm.default' name}}
+ {{else}}
+ {{tp 'quality_gates.delete.confirm.message' name}}
+ {{/if}}
+ </div>
+ <div class="modal-foot">
+ <button id="delete-gate-submit">{{t 'delete'}}</button>
+ <a href="#" class="js-modal-close" id="delete-gate-cancel">{{t 'cancel'}}</a>
+ </div>
+</form>
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-sidebar-list-item.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-gate.hbs
index 2e92b2eedde..d39ea6809de 100644
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-sidebar-list-item.hbs
+++ b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-gate.hbs
@@ -2,7 +2,7 @@
<tr>
<td class="text-top">{{name}}</td>
<td class="text-top thin nowrap spacer-left">
- {{#if default}}
+ {{#if isDefault}}
<span class="badge pull-right">{{t 'default'}}</span>
{{/if}}
</td>
diff --git a/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-gates.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-gates.hbs
new file mode 100644
index 00000000000..8022059ffad
--- /dev/null
+++ b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-gates.hbs
@@ -0,0 +1 @@
+<div class="js-list"></div>
diff --git a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gates-layout.hbs b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-layout.hbs
index 3428ac4d466..3428ac4d466 100644
--- a/server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gates-layout.hbs
+++ b/server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-layout.hbs
diff --git a/server/sonar-web/src/main/js/components/common/handlebars-extensions.js b/server/sonar-web/src/main/js/components/common/handlebars-extensions.js
index a92f2d19026..cec8c0c725b 100644
--- a/server/sonar-web/src/main/js/components/common/handlebars-extensions.js
+++ b/server/sonar-web/src/main/js/components/common/handlebars-extensions.js
@@ -434,7 +434,7 @@
}, '');
});
- Handlebars.registerHelper('operators', function (metricType, options) {
+ Handlebars.registerHelper('operators', function (options) {
var ops = ['LT', 'GT', 'EQ', 'NE'];
return ops.reduce(function (prev, current) {
diff --git a/server/sonar-web/src/main/less/init/forms.less b/server/sonar-web/src/main/less/init/forms.less
index 4403489ccf6..656491eb0b6 100644
--- a/server/sonar-web/src/main/less/init/forms.less
+++ b/server/sonar-web/src/main/less/init/forms.less
@@ -178,9 +178,10 @@ input[type=button] {
}
}
-.input-small {
- width: 80px;
-}
+.input-tiny { width: 60px; }
+.input-small { width: 100px; }
+.input-medium { width: 150px; }
+.input-large { width: 200px; }
em.mandatory {
color: #990000;
diff --git a/server/sonar-web/src/main/less/init/icons.less b/server/sonar-web/src/main/less/init/icons.less
index e94718f0f4f..ebe75c89e22 100644
--- a/server/sonar-web/src/main/less/init/icons.less
+++ b/server/sonar-web/src/main/less/init/icons.less
@@ -178,21 +178,25 @@ a[class^="icon-"], a[class*=" icon-"] {
* Alert
*/
+.icon-alert-ok { font-size: @iconFontSize; }
.icon-alert-ok:before {
content: "\f013";
color: @green;
font-size: @iconFontSize;
}
+.icon-alert-warn { font-size: @iconFontSize; }
.icon-alert-warn:before {
content: "\f000";
color: @orange;
font-size: @iconFontSize;
}
+.icon-alert-error { font-size: @iconFontSize; }
.icon-alert-error:before {
content: "\f057";
color: @red;
font-size: @iconFontSize;
}
+.icon-alert-none { font-size: @iconFontSize; }
.icon-alert-none:before {
content: "\f056";
color: @middleGrey;
diff --git a/server/sonar-web/src/main/less/init/misc.less b/server/sonar-web/src/main/less/init/misc.less
index 3cc8526e54d..e369019dc77 100644
--- a/server/sonar-web/src/main/less/init/misc.less
+++ b/server/sonar-web/src/main/less/init/misc.less
@@ -79,6 +79,7 @@ td.spacer-top { padding-top: 8px; }
.width-25 { width: 25%; }
.width-20 { width: 20%; }
.width-15 { width: 15%; }
+.width-10 { width: 10%; }
.abs-width-400 { width: 400px; }
diff --git a/server/sonar-web/src/main/less/init/tables.less b/server/sonar-web/src/main/less/init/tables.less
index aca9725e965..991005bde79 100644
--- a/server/sonar-web/src/main/less/init/tables.less
+++ b/server/sonar-web/src/main/less/init/tables.less
@@ -91,7 +91,7 @@ table.data.zebra tbody tr:nth-child(odd) {
}
.data input, .data select, .data button {
- vertical-align: baseline;
+ vertical-align: middle;
}
.data td.category {
diff --git a/server/sonar-web/src/main/webapp/WEB-INF/app/views/quality_gates/index.html.erb b/server/sonar-web/src/main/webapp/WEB-INF/app/views/quality_gates/index.html.erb
index 588f7c3ba25..749c6aeba6a 100644
--- a/server/sonar-web/src/main/webapp/WEB-INF/app/views/quality_gates/index.html.erb
+++ b/server/sonar-web/src/main/webapp/WEB-INF/app/views/quality_gates/index.html.erb
@@ -1,5 +1,6 @@
-<% content_for :script do %>
- <script>require(['apps/quality-gate/app']);</script>
-<% end %>
-
-<div class="search-navigator" id="quality-gates"></div>
+<div class="search-navigator sticky search-navigator-extended-view" id="quality-gates"></div>
+<script>
+ require(['apps/quality-gates/app'], function (App) {
+ App.start({ el: '#quality-gates' });
+ });
+</script>
diff --git a/server/sonar-web/src/test/js/quality-gates-spec.js b/server/sonar-web/src/test/js/quality-gates-spec.js
index 6c1d34cd6f5..207e8a2cbec 100644
--- a/server/sonar-web/src/test/js/quality-gates-spec.js
+++ b/server/sonar-web/src/test/js/quality-gates-spec.js
@@ -38,21 +38,23 @@ casper.test.begin(testName('Should Show List'), 5, function (test) {
.then(function () {
casper.evaluate(function () {
- require(['apps/quality-gate/app']);
+ require(['apps/quality-gates/app'], function (App) {
+ App.start({ el: '#quality-gates' });
+ });
});
})
.then(function () {
- casper.waitForSelector('.quality-gates-results .list-group-item');
+ casper.waitForSelector('.js-list .list-group-item');
})
.then(function () {
- test.assertElementCount('.quality-gates-results .list-group-item', 3);
- test.assertSelectorContains('.quality-gates-results .list-group-item', 'SonarQube way');
- test.assertSelectorContains('.quality-gates-results .list-group-item', 'Simple Gate');
- test.assertSelectorContains('.quality-gates-results .list-group-item', 'Another Gate');
+ test.assertElementCount('.js-list .list-group-item', 3);
+ test.assertSelectorContains('.js-list .list-group-item', 'SonarQube way');
+ test.assertSelectorContains('.js-list .list-group-item', 'Simple Gate');
+ test.assertSelectorContains('.js-list .list-group-item', 'Another Gate');
- test.assertElementCount('.quality-gates-results .badge', 1);
+ test.assertElementCount('.js-list .badge', 1);
})
.then(function () {
@@ -65,35 +67,36 @@ casper.test.begin(testName('Should Show List'), 5, function (test) {
});
-casper.test.begin(testName('Should Show Details', 'Anonymous'), 12, function (test) {
+casper.test.begin(testName('Should Show Details', 'Anonymous'), 14, function (test) {
casper
.start(lib.buildUrl('quality_gates'), function () {
lib.setDefaultViewport();
lib.mockRequestFromFile('/api/qualitygates/app', 'app-anonymous.json');
lib.mockRequestFromFile('/api/qualitygates/list', 'list.json');
- lib.mockRequestFromFile('/api/qualitygates/show?id=1', 'show.json');
+ lib.mockRequestFromFile('/api/qualitygates/show', 'show.json', { data: { id: '1' } });
})
.then(function () {
casper.evaluate(function () {
- require(['apps/quality-gate/app']);
+ require(['apps/quality-gates/app'], function (App) {
+ App.start({ el: '#quality-gates' });
+ });
});
})
.then(function () {
- casper.waitForSelector('.quality-gates-results .list-group-item');
+ casper.waitForSelector('.js-list .list-group-item');
})
.then(function () {
- // FIXME use better selector
- casper.click('.quality-gates-results .list-group-item:last-child');
+ casper.click('.js-list .list-group-item[data-id="1"]');
casper.waitForSelector('.search-navigator-header-component');
})
.then(function () {
- test.assertElementCount('.quality-gates-results .list-group-item.active', 1);
- test.assertSelectorContains('.quality-gates-results .list-group-item.active', 'SonarQube way');
+ test.assertElementCount('.js-list .list-group-item.active', 1);
+ test.assertSelectorContains('.js-list .list-group-item.active', 'SonarQube way');
test.assertSelectorContains('.search-navigator-workspace-header', 'SonarQube way');
test.assertDoesntExist('#quality-gate-rename');
@@ -101,15 +104,21 @@ casper.test.begin(testName('Should Show Details', 'Anonymous'), 12, function (te
test.assertDoesntExist('#quality-gate-unset-as-default');
test.assertDoesntExist('#quality-gate-delete');
- test.assertExists('.quality-gate-conditions');
- test.assertElementCount('.quality-gate-conditions tbody tr', 8);
- test.assertDoesntExist('.quality-gate-conditions .update-condition');
- test.assertDoesntExist('.quality-gate-conditions .delete-condition');
+ test.assertExists('.js-conditions');
+ test.assertElementCount('.js-conditions tbody tr', 8);
+ test.assertDoesntExist('.js-conditions .update-condition');
+ test.assertDoesntExist('.js-conditions .delete-condition');
test.assertExists('.quality-gate-default-message');
})
.then(function () {
+ test.assertNotVisible('.js-more');
+ casper.click('.js-show-more');
+ test.assertVisible('.js-more');
+ })
+
+ .then(function () {
lib.sendCoverage();
})
@@ -126,39 +135,40 @@ casper.test.begin(testName('Should Show Details', 'Admin'), 12, function (test)
lib.mockRequestFromFile('/api/qualitygates/app', 'app.json');
lib.mockRequestFromFile('/api/qualitygates/list', 'list.json');
- lib.mockRequestFromFile('/api/qualitygates/show?id=1', 'show.json');
+ lib.mockRequestFromFile('/api/qualitygates/show', 'show.json', { data: { id: '1' } });
})
.then(function () {
casper.evaluate(function () {
- require(['apps/quality-gate/app']);
+ require(['apps/quality-gates/app'], function (App) {
+ App.start({ el: '#quality-gates' });
+ });
});
})
.then(function () {
- casper.waitForSelector('.quality-gates-results .list-group-item');
+ casper.waitForSelector('.js-list .list-group-item');
})
.then(function () {
- // FIXME use better selector
- casper.click('.quality-gates-results .list-group-item:last-child');
+ casper.click('.js-list .list-group-item[data-id="1"]');
casper.waitForSelector('.search-navigator-header-component');
})
.then(function () {
- test.assertElementCount('.quality-gates-results .list-group-item.active', 1);
- test.assertSelectorContains('.quality-gates-results .list-group-item.active', 'SonarQube way');
+ test.assertElementCount('.js-list .list-group-item.active', 1);
+ test.assertSelectorContains('.js-list .list-group-item.active', 'SonarQube way');
test.assertSelectorContains('.search-navigator-workspace-header', 'SonarQube way');
test.assertExists('#quality-gate-rename');
test.assertExists('#quality-gate-copy');
- test.assertExists('#quality-gate-unset-as-default');
+ test.assertExists('#quality-gate-toggle-default');
test.assertExists('#quality-gate-delete');
- test.assertExists('.quality-gate-conditions');
- test.assertElementCount('.quality-gate-conditions tbody tr', 8);
- test.assertElementCount('.quality-gate-conditions .update-condition', 8);
- test.assertElementCount('.quality-gate-conditions .delete-condition', 8);
+ test.assertExists('.js-conditions');
+ test.assertElementCount('.js-conditions tbody tr', 8);
+ test.assertElementCount('.js-conditions .update-condition', 8);
+ test.assertElementCount('.js-conditions .delete-condition', 8);
test.assertExists('.quality-gate-default-message');
})
@@ -173,6 +183,44 @@ casper.test.begin(testName('Should Show Details', 'Admin'), 12, function (test)
});
+casper.test.begin(testName('Should Show Projects'), 2, function (test) {
+ casper
+ .start(lib.buildUrl('quality_gates#show/5'), function () {
+ lib.setDefaultViewport();
+
+ lib.mockRequestFromFile('/api/qualitygates/app', 'app-anonymous.json');
+ lib.mockRequestFromFile('/api/qualitygates/list', 'list.json');
+ lib.mockRequestFromFile('/api/qualitygates/show', 'show-another.json', { data: { id: '5' } });
+ lib.mockRequestFromFile('/api/qualitygates/search?gateId=5', 'projects.json');
+ })
+
+ .then(function () {
+ casper.evaluate(function () {
+ require(['apps/quality-gates/app'], function (App) {
+ App.start({ el: '#quality-gates' });
+ });
+ });
+ })
+
+ .then(function () {
+ casper.waitForSelector('.select-list-list li');
+ })
+
+ .then(function () {
+ test.assertElementCount('.select-list-list li', 1);
+ test.assertSelectorContains('.select-list-list li', 'SonarQube');
+ })
+
+ .then(function () {
+ lib.sendCoverage();
+ })
+
+ .run(function () {
+ test.done();
+ });
+});
+
+
casper.test.begin(testName('Should Rename'), 2, function (test) {
casper
.start(lib.buildUrl('quality_gates#show/1'), function () {
@@ -180,13 +228,15 @@ casper.test.begin(testName('Should Rename'), 2, function (test) {
lib.mockRequestFromFile('/api/qualitygates/app', 'app.json');
lib.mockRequestFromFile('/api/qualitygates/list', 'list.json');
- lib.mockRequestFromFile('/api/qualitygates/show?id=1', 'show.json');
+ lib.mockRequestFromFile('/api/qualitygates/show', 'show.json', { data: { id: '1' } });
lib.mockRequestFromFile('/api/qualitygates/rename', 'rename.json', { data: { id: '1', name: 'New Name' } });
})
.then(function () {
casper.evaluate(function () {
- require(['apps/quality-gate/app']);
+ require(['apps/quality-gates/app'], function (App) {
+ App.start({ el: '#quality-gates' });
+ });
jQuery.ajaxSetup({ dataType: 'json' });
});
})
@@ -197,12 +247,12 @@ casper.test.begin(testName('Should Rename'), 2, function (test) {
.then(function () {
casper.click('#quality-gate-rename');
- casper.waitUntilVisible('#quality-gate-edit-name');
+ casper.waitUntilVisible('#quality-gate-form-name');
})
.then(function () {
casper.evaluate(function () {
- jQuery('#quality-gate-edit-name').val('New Name');
+ jQuery('#quality-gate-form-name').val('New Name');
});
casper.click('.modal-foot button');
casper.waitForSelectorTextChange('.search-navigator-header-component');
@@ -210,7 +260,7 @@ casper.test.begin(testName('Should Rename'), 2, function (test) {
.then(function () {
test.assertSelectorContains('.search-navigator-header-component', 'New Name');
- test.assertSelectorContains('.quality-gates-results .list-group-item.active', 'New Name');
+ test.assertSelectorContains('.js-list .list-group-item.active', 'New Name');
})
.then(function () {
@@ -230,13 +280,16 @@ casper.test.begin(testName('Should Copy'), 3, function (test) {
lib.mockRequestFromFile('/api/qualitygates/app', 'app.json');
lib.mockRequestFromFile('/api/qualitygates/list', 'list.json');
- lib.mockRequestFromFile('/api/qualitygates/show?id=1', 'show.json');
+ lib.mockRequestFromFile('/api/qualitygates/show', 'show.json', { data: { id: '1' } });
+ lib.mockRequestFromFile('/api/qualitygates/show', 'show-created.json', { data: { id: '6' } });
lib.mockRequestFromFile('/api/qualitygates/copy', 'copy.json', { data: { id: '1', name: 'New Name' } });
})
.then(function () {
casper.evaluate(function () {
- require(['apps/quality-gate/app']);
+ require(['apps/quality-gates/app'], function (App) {
+ App.start({ el: '#quality-gates' });
+ });
jQuery.ajaxSetup({ dataType: 'json' });
});
})
@@ -247,12 +300,12 @@ casper.test.begin(testName('Should Copy'), 3, function (test) {
.then(function () {
casper.click('#quality-gate-copy');
- casper.waitUntilVisible('#quality-gate-edit-name');
+ casper.waitUntilVisible('#quality-gate-form-name');
})
.then(function () {
casper.evaluate(function () {
- jQuery('#quality-gate-edit-name').val('New Name');
+ jQuery('#quality-gate-form-name').val('New Name');
});
casper.click('.modal-foot button');
casper.waitForSelectorTextChange('.search-navigator-header-component');
@@ -260,8 +313,8 @@ casper.test.begin(testName('Should Copy'), 3, function (test) {
.then(function () {
test.assertSelectorContains('.search-navigator-header-component', 'New Name');
- test.assertSelectorContains('.quality-gates-results .list-group-item.active', 'New Name');
- test.assertSelectorContains('.quality-gates-results .list-group-item', 'SonarQube way');
+ test.assertSelectorContains('.js-list .list-group-item.active', 'New Name');
+ test.assertSelectorContains('.js-list .list-group-item', 'SonarQube way');
})
.then(function () {
@@ -281,13 +334,15 @@ casper.test.begin(testName('Should Set As Default'), 4, function (test) {
lib.mockRequestFromFile('/api/qualitygates/app', 'app.json');
lib.mockRequestFromFile('/api/qualitygates/list', 'list.json');
- lib.mockRequestFromFile('/api/qualitygates/show?id=5', 'show-another.json');
+ lib.mockRequestFromFile('/api/qualitygates/show', 'show-another.json', { data: { id: '5' } });
lib.mockRequest('/api/qualitygates/set_as_default', '{}', { data: { id: '5' } });
})
.then(function () {
casper.evaluate(function () {
- require(['apps/quality-gate/app']);
+ require(['apps/quality-gates/app'], function (App) {
+ App.start({ el: '#quality-gates' });
+ });
jQuery.ajaxSetup({ dataType: 'json' });
});
})
@@ -297,15 +352,15 @@ casper.test.begin(testName('Should Set As Default'), 4, function (test) {
})
.then(function () {
- test.assertDoesntExist('.quality-gates-results .list-group-item.active .badge');
+ test.assertDoesntExist('.js-list .list-group-item.active .badge');
test.assertDoesntExist('.quality-gate-default-message');
- casper.click('#quality-gate-set-as-default');
- casper.waitForSelector('.quality-gates-results .list-group-item.active .badge');
+ casper.click('#quality-gate-toggle-default');
+ casper.waitForSelector('.js-list .list-group-item.active .badge');
})
.then(function () {
test.assertExists('.quality-gate-default-message');
- test.assertElementCount('.quality-gates-results .badge', 1);
+ test.assertElementCount('.js-list .badge', 1);
})
.then(function () {
@@ -325,13 +380,15 @@ casper.test.begin(testName('Should Unset As Default'), 4, function (test) {
lib.mockRequestFromFile('/api/qualitygates/app', 'app.json');
lib.mockRequestFromFile('/api/qualitygates/list', 'list.json');
- lib.mockRequestFromFile('/api/qualitygates/show?id=1', 'show.json');
- lib.mockRequest('/api/qualitygates/unset_default', '{}');
+ lib.mockRequestFromFile('/api/qualitygates/show', 'show.json', { data: { id: '1' } });
+ lib.mockRequest('/api/qualitygates/unset_default', '{}', { data: { id: '1' } });
})
.then(function () {
casper.evaluate(function () {
- require(['apps/quality-gate/app']);
+ require(['apps/quality-gates/app'], function (App) {
+ App.start({ el: '#quality-gates' });
+ });
jQuery.ajaxSetup({ dataType: 'json' });
});
})
@@ -341,15 +398,15 @@ casper.test.begin(testName('Should Unset As Default'), 4, function (test) {
})
.then(function () {
- test.assertExists('.quality-gates-results .list-group-item.active .badge');
+ test.assertExists('.js-list .list-group-item.active .badge');
test.assertExists('.quality-gate-default-message');
- casper.click('#quality-gate-unset-as-default');
- casper.waitWhileSelector('.quality-gates-results .list-group-item.active .badge');
+ casper.click('#quality-gate-toggle-default');
+ casper.waitWhileSelector('.js-list .list-group-item.active .badge');
})
.then(function () {
test.assertDoesntExist('.quality-gate-default-message');
- test.assertDoesntExist('.quality-gates-results .badge');
+ test.assertDoesntExist('.js-list .badge');
})
.then(function () {
@@ -369,29 +426,41 @@ casper.test.begin(testName('Should Create'), 2, function (test) {
lib.mockRequestFromFile('/api/qualitygates/app', 'app.json');
lib.mockRequestFromFile('/api/qualitygates/list', 'list.json');
+ lib.mockRequest('/api/qualitygates/create', '{"errors":[{"msg": "error"}]}',
+ { status: 400, data: { name: 'Bad' } });
lib.mockRequestFromFile('/api/qualitygates/create', 'create.json', { data: { name: 'New Name' } });
- lib.mockRequestFromFile('/api/qualitygates/show?id=6', 'show-created.json');
+ lib.mockRequestFromFile('/api/qualitygates/show', 'show-created.json', { data: { id: '6' } });
})
.then(function () {
casper.evaluate(function () {
- require(['apps/quality-gate/app']);
+ require(['apps/quality-gates/app'], function (App) {
+ App.start({ el: '#quality-gates' });
+ });
jQuery.ajaxSetup({ dataType: 'json' });
});
})
.then(function () {
- casper.waitForSelector('.quality-gates-results .list-group-item');
+ casper.waitForSelector('.js-list .list-group-item');
})
.then(function () {
casper.click('#quality-gate-add');
- casper.waitUntilVisible('#quality-gate-edit-name');
+ casper.waitUntilVisible('#quality-gate-form-name');
+ })
+
+ .then(function () {
+ casper.evaluate(function () {
+ jQuery('#quality-gate-form-name').val('Bad');
+ });
+ casper.click('.modal-foot button');
+ casper.waitForSelector('.alert-danger');
})
.then(function () {
casper.evaluate(function () {
- jQuery('#quality-gate-edit-name').val('New Name');
+ jQuery('#quality-gate-form-name').val('New Name');
});
casper.click('.modal-foot button');
casper.waitForSelector('.search-navigator-header-component');
@@ -399,7 +468,7 @@ casper.test.begin(testName('Should Create'), 2, function (test) {
.then(function () {
test.assertSelectorContains('.search-navigator-header-component', 'New Name');
- test.assertSelectorContains('.quality-gates-results .list-group-item.active', 'New Name');
+ test.assertSelectorContains('.js-list .list-group-item.active', 'New Name');
})
.then(function () {
@@ -419,13 +488,16 @@ casper.test.begin(testName('Should Delete'), 2, function (test) {
lib.mockRequestFromFile('/api/qualitygates/app', 'app.json');
lib.mockRequestFromFile('/api/qualitygates/list', 'list.json');
- lib.mockRequestFromFile('/api/qualitygates/show?id=5', 'show-another.json');
- lib.mockRequest('/api/qualitygates/destroy', '{}', { data: { id: '5' } });
+ lib.mockRequestFromFile('/api/qualitygates/show', 'show-another.json', { data: { id: '5' } });
+ this.deleteMock = lib.mockRequest('/api/qualitygates/destroy', '{"errors":[{"msg": "error"}]}',
+ { status: 400 });
})
.then(function () {
casper.evaluate(function () {
- require(['apps/quality-gate/app']);
+ require(['apps/quality-gates/app'], function (App) {
+ App.start({ el: '#quality-gates' });
+ });
jQuery.ajaxSetup({ dataType: 'json' });
});
})
@@ -435,18 +507,26 @@ casper.test.begin(testName('Should Delete'), 2, function (test) {
})
.then(function () {
- test.assertElementCount('.quality-gates-results .list-group-item', 3);
+ test.assertElementCount('.js-list .list-group-item', 3);
casper.click('#quality-gate-delete');
- casper.waitForSelector('button[data-confirm="yes"]');
+ casper.waitForSelector('#delete-gate-submit');
+ })
+
+ .then(function () {
+ casper.click('#delete-gate-submit');
+ casper.waitForSelector('.alert-danger');
})
.then(function () {
- casper.click('button[data-confirm="yes"]');
+ lib.clearRequestMock(this.deleteMock);
+ lib.mockRequest('/api/qualitygates/destroy', '{}', { data: { id: '5' } });
+
+ casper.click('#delete-gate-submit');
casper.waitWhileSelector('.search-navigator-header-component');
})
.then(function () {
- test.assertElementCount('.quality-gates-results .list-group-item', 2);
+ test.assertElementCount('.js-list .list-group-item', 2);
})
.then(function () {
@@ -466,14 +546,16 @@ casper.test.begin(testName('Should Add Condition'), 6, function (test) {
lib.mockRequestFromFile('/api/qualitygates/app', 'app.json');
lib.mockRequestFromFile('/api/qualitygates/list', 'list.json');
- lib.mockRequestFromFile('/api/qualitygates/show?id=5', 'show-another.json');
+ lib.mockRequestFromFile('/api/qualitygates/show', 'show-another.json', { data: { id: '5' } });
lib.mockRequestFromFile('/api/qualitygates/create_condition', 'create-condition.json',
- { data: { metric: 'complexity', op: 'GT', period: '1', warning: '3', error: '4' } });
+ { data: { gateId: '5', metric: 'complexity', op: 'GT', period: '1', warning: '3', error: '4' } });
})
.then(function () {
casper.evaluate(function () {
- require(['apps/quality-gate/app']);
+ require(['apps/quality-gates/app'], function (App) {
+ App.start({ el: '#quality-gates' });
+ });
jQuery.ajaxSetup({ dataType: 'json' });
});
})
@@ -483,12 +565,12 @@ casper.test.begin(testName('Should Add Condition'), 6, function (test) {
})
.then(function () {
- test.assertElementCount('.quality-gate-conditions [name="error"]', 0);
+ test.assertElementCount('.js-conditions [name="error"]', 0);
casper.evaluate(function () {
jQuery('#quality-gate-new-condition-metric').val('complexity').change();
});
- test.assertElementCount('.quality-gate-conditions [name="error"]', 1);
+ test.assertElementCount('.js-conditions [name="error"]', 1);
})
.then(function () {
@@ -497,12 +579,12 @@ casper.test.begin(testName('Should Add Condition'), 6, function (test) {
})
.then(function () {
- test.assertElementCount('.quality-gate-conditions [name="error"]', 0);
+ test.assertElementCount('.js-conditions [name="error"]', 0);
casper.evaluate(function () {
jQuery('#quality-gate-new-condition-metric').val('complexity').change();
});
- test.assertElementCount('.quality-gate-conditions [name="error"]', 1);
+ test.assertElementCount('.js-conditions [name="error"]', 1);
casper.evaluate(function () {
jQuery('[name="period"]').val('1');
@@ -536,14 +618,16 @@ casper.test.begin(testName('Should Update Condition'), 3, function (test) {
lib.mockRequestFromFile('/api/qualitygates/app', 'app.json');
lib.mockRequestFromFile('/api/qualitygates/list', 'list.json');
- lib.mockRequestFromFile('/api/qualitygates/show?id=1', 'show.json');
+ lib.mockRequestFromFile('/api/qualitygates/show', 'show.json', { data: { id: '1' } });
lib.mockRequestFromFile('/api/qualitygates/update_condition', 'update-condition.json',
{ data: { id: '1', warning: '173' } });
})
.then(function () {
casper.evaluate(function () {
- require(['apps/quality-gate/app']);
+ require(['apps/quality-gates/app'], function (App) {
+ App.start({ el: '#quality-gates' });
+ });
jQuery.ajaxSetup({ dataType: 'json' });
});
})
@@ -553,17 +637,17 @@ casper.test.begin(testName('Should Update Condition'), 3, function (test) {
})
.then(function () {
- test.assertExists('.quality-gate-conditions tr:first-child .update-condition[disabled]');
+ test.assertExists('.js-conditions tr:first-child .update-condition[disabled]');
casper.evaluate(function () {
- jQuery('.quality-gate-conditions tr:first-child [name="warning"]').val('173').change();
+ jQuery('.js-conditions tr:first-child [name="warning"]').val('173').change();
});
- test.assertDoesntExist('.quality-gate-conditions tr:first-child .update-condition[disabled]');
- casper.click('.quality-gate-conditions tr:first-child .update-condition');
- casper.waitWhileSelector('.quality-gate-conditions tr:first-child .update-condition:not([disabled])');
+ test.assertDoesntExist('.js-conditions tr:first-child .update-condition[disabled]');
+ casper.click('.js-conditions tr:first-child .update-condition');
+ casper.waitWhileSelector('.js-conditions tr:first-child .update-condition:not([disabled])');
})
.then(function () {
- test.assertExists('.quality-gate-conditions tr:first-child .update-condition[disabled]');
+ test.assertExists('.js-conditions tr:first-child .update-condition[disabled]');
})
.then(function () {
@@ -583,13 +667,16 @@ casper.test.begin(testName('Should Delete Condition'), 2, function (test) {
lib.mockRequestFromFile('/api/qualitygates/app', 'app.json');
lib.mockRequestFromFile('/api/qualitygates/list', 'list.json');
- lib.mockRequestFromFile('/api/qualitygates/show?id=1', 'show.json');
- lib.mockRequest('/api/qualitygates/delete_condition', '{}', { data: { id: '1' } });
+ lib.mockRequestFromFile('/api/qualitygates/show', 'show.json', { data: { id: '1' } });
+ this.deleteMock = lib.mockRequest('/api/qualitygates/delete_condition', '{"errors":[{"msg": "error"}]}',
+ { status: 400 });
})
.then(function () {
casper.evaluate(function () {
- require(['apps/quality-gate/app']);
+ require(['apps/quality-gates/app'], function (App) {
+ App.start({ el: '#quality-gates' });
+ });
jQuery.ajaxSetup({ dataType: 'json' });
});
})
@@ -601,12 +688,19 @@ casper.test.begin(testName('Should Delete Condition'), 2, function (test) {
.then(function () {
test.assertElementCount('.delete-condition', 8);
- casper.click('.quality-gate-conditions tr:first-child .delete-condition');
- casper.waitForSelector('button[data-confirm="yes"]');
+ casper.click('.js-conditions tr:first-child .delete-condition');
+ casper.waitForSelector('#delete-condition-submit');
+ })
+
+ .then(function () {
+ casper.click('#delete-condition-submit');
+ casper.waitForSelector('.alert-danger');
})
.then(function () {
- casper.click('button[data-confirm="yes"]');
+ lib.clearRequestMock(this.deleteMock);
+ lib.mockRequest('/api/qualitygates/delete_condition', '{}', { data: { id: '1' } });
+ casper.click('#delete-condition-submit');
lib.waitForElementCount('.delete-condition', 7);
})
diff --git a/server/sonar-web/src/test/json/quality-gates-spec/projects.json b/server/sonar-web/src/test/json/quality-gates-spec/projects.json
new file mode 100644
index 00000000000..1d75a5e35b8
--- /dev/null
+++ b/server/sonar-web/src/test/json/quality-gates-spec/projects.json
@@ -0,0 +1,10 @@
+{
+ "more": false,
+ "results": [
+ {
+ "id": 191,
+ "name": "SonarQube",
+ "selected": true
+ }
+ ]
+}
diff --git a/server/sonar-web/src/test/views/quality_gates.jade b/server/sonar-web/src/test/views/quality_gates.jade
index c7cbf2c021a..946edd3b8fb 100644
--- a/server/sonar-web/src/test/views/quality_gates.jade
+++ b/server/sonar-web/src/test/views/quality_gates.jade
@@ -2,4 +2,4 @@ extends layouts/main
block body
#content
- .search-navigator#quality-gates
+ .search-navigator.sticky.search-navigator-extended-view#quality-gates