From 456991a9a6292cfd88b053d808066f541e847028 Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Tue, 26 May 2015 15:58:33 +0200 Subject: [PATCH] refactor quality gates page --- server/sonar-web/Gruntfile.coffee | 6 +- .../main/coffee/apps/quality-gate/app.coffee | 118 -------- .../collections/conditions.coffee | 29 -- .../collections/quality-gates.coffee | 48 ---- .../coffee/apps/quality-gate/layout.coffee | 42 --- .../apps/quality-gate/models/condition.coffee | 58 ---- .../quality-gate/models/quality-gate.coffee | 26 -- .../coffee/apps/quality-gate/router.coffee | 62 ---- .../quality-gate-detail-condition.hbs | 62 ---- .../quality-gate-detail-conditions-empty.hbs | 3 - .../quality-gate-detail-conditions.hbs | 45 --- .../templates/quality-gate-edit.hbs | 22 -- .../quality-gate-sidebar-list-empty.hbs | 1 - .../views/quality-gate-actions-view.coffee | 44 --- .../quality-gate-detail-condition-view.coffee | 146 ---------- ...y-gate-detail-conditions-empty-view.coffee | 27 -- ...quality-gate-detail-conditions-view.coffee | 105 ------- .../quality-gate-detail-header-view.coffee | 101 ------- .../quality-gate-detail-projects-view.coffee | 55 ---- .../views/quality-gate-detail-view.coffee | 67 ----- .../views/quality-gate-edit-view.coffee | 101 ------- ...uality-gate-sidebar-list-empty-view.coffee | 28 -- ...quality-gate-sidebar-list-item-view.coffee | 44 --- .../quality-gate-sidebar-list-view.coffee | 42 --- .../js/apps/quality-gates/actions-view.js | 27 ++ .../src/main/js/apps/quality-gates/app.js | 69 +++++ .../main/js/apps/quality-gates/condition.js | 47 +++ .../main/js/apps/quality-gates/conditions.js | 10 + .../main/js/apps/quality-gates/controller.js | 65 +++++ .../main/js/apps/quality-gates/copy-view.js | 24 ++ .../main/js/apps/quality-gates/create-view.js | 24 ++ .../main/js/apps/quality-gates/delete-view.js | 33 +++ .../js/apps/quality-gates/details-view.js | 46 +++ .../main/js/apps/quality-gates/form-view.js | 47 +++ .../apps/quality-gates/gate-condition-view.js | 100 +++++++ .../gate-conditions-delete-view.js | 39 +++ .../gate-conditions-empty-view.js | 16 ++ .../quality-gates/gate-conditions-view.js | 75 +++++ .../apps/quality-gates/gate-projects-view.js | 48 ++++ .../main/js/apps/quality-gates/gate-view.js | 29 ++ .../src/main/js/apps/quality-gates/gate.js | 54 ++++ .../main/js/apps/quality-gates/gates-view.js | 25 ++ .../src/main/js/apps/quality-gates/gates.js | 30 ++ .../main/js/apps/quality-gates/header-view.js | 52 ++++ .../src/main/js/apps/quality-gates/layout.js | 24 ++ .../main/js/apps/quality-gates/rename-view.js | 23 ++ .../src/main/js/apps/quality-gates/router.js | 22 ++ .../templates/_quality-gate-intro.hbs | 0 .../templates/quality-gate-actions.hbs | 0 .../quality-gate-detail-condition.hbs | 69 +++++ .../quality-gate-detail-conditions-empty.hbs | 3 + .../quality-gate-detail-conditions.hbs | 39 +++ .../templates/quality-gate-detail-header.hbs | 8 +- .../quality-gate-detail-projects.hbs | 4 +- .../templates/quality-gate-detail.hbs | 0 .../templates/quality-gate-form.hbs | 20 ++ .../quality-gates-condition-delete.hbs | 13 + .../templates/quality-gates-delete.hbs | 17 ++ .../templates/quality-gates-gate.hbs} | 2 +- .../templates/quality-gates-gates.hbs | 1 + .../templates/quality-gates-layout.hbs | 0 .../common/handlebars-extensions.js | 2 +- .../sonar-web/src/main/less/init/forms.less | 7 +- .../sonar-web/src/main/less/init/icons.less | 4 + server/sonar-web/src/main/less/init/misc.less | 1 + .../sonar-web/src/main/less/init/tables.less | 2 +- .../app/views/quality_gates/index.html.erb | 11 +- .../src/test/js/quality-gates-spec.js | 272 ++++++++++++------ .../json/quality-gates-spec/projects.json | 10 + .../src/test/views/quality_gates.jade | 2 +- 70 files changed, 1311 insertions(+), 1387 deletions(-) delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/app.coffee delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/collections/conditions.coffee delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/collections/quality-gates.coffee delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/layout.coffee delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/models/condition.coffee delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/models/quality-gate.coffee delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/router.coffee delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-condition.hbs delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-conditions-empty.hbs delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-conditions.hbs delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-edit.hbs delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-sidebar-list-empty.hbs delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-actions-view.coffee delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-condition-view.coffee delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-conditions-empty-view.coffee delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-conditions-view.coffee delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-header-view.coffee delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-projects-view.coffee delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-detail-view.coffee delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-edit-view.coffee delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-empty-view.coffee delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-item-view.coffee delete mode 100644 server/sonar-web/src/main/coffee/apps/quality-gate/views/quality-gate-sidebar-list-view.coffee create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/actions-view.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/app.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/condition.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/conditions.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/controller.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/copy-view.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/create-view.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/delete-view.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/details-view.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/form-view.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/gate-condition-view.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-delete-view.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-empty-view.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/gate-conditions-view.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/gate-projects-view.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/gate-view.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/gate.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/gates-view.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/gates.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/header-view.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/layout.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/rename-view.js create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/router.js rename server/sonar-web/src/main/{coffee/apps/quality-gate => js/apps/quality-gates}/templates/_quality-gate-intro.hbs (100%) rename server/sonar-web/src/main/{coffee/apps/quality-gate => js/apps/quality-gates}/templates/quality-gate-actions.hbs (100%) create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-condition.hbs create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-conditions-empty.hbs create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-detail-conditions.hbs rename server/sonar-web/src/main/{coffee/apps/quality-gate => js/apps/quality-gates}/templates/quality-gate-detail-header.hbs (64%) rename server/sonar-web/src/main/{coffee/apps/quality-gate => js/apps/quality-gates}/templates/quality-gate-detail-projects.hbs (92%) rename server/sonar-web/src/main/{coffee/apps/quality-gate => js/apps/quality-gates}/templates/quality-gate-detail.hbs (100%) create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gate-form.hbs create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-condition-delete.hbs create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-delete.hbs rename server/sonar-web/src/main/{coffee/apps/quality-gate/templates/quality-gate-sidebar-list-item.hbs => js/apps/quality-gates/templates/quality-gates-gate.hbs} (89%) create mode 100644 server/sonar-web/src/main/js/apps/quality-gates/templates/quality-gates-gates.hbs rename server/sonar-web/src/main/{coffee/apps/quality-gate => js/apps/quality-gates}/templates/quality-gates-layout.hbs (100%) create mode 100644 server/sonar-web/src/test/json/quality-gates-spec/projects.json diff --git a/server/sonar-web/Gruntfile.coffee b/server/sonar-web/Gruntfile.coffee index ac2a0b1bbf7..a8acf348a72 100644 --- a/server/sonar-web/Gruntfile.coffee +++ b/server/sonar-web/Gruntfile.coffee @@ -140,7 +140,7 @@ module.exports = (grunt) -> { grunt: true, args: ['requirejs:app', '--app=measures'] } { grunt: true, args: ['requirejs:app', '--app=nav'] } { grunt: true, args: ['requirejs:app', '--app=provisioning'] } - { grunt: true, args: ['requirejs:app', '--app=quality-gate'] } + { grunt: true, args: ['requirejs:app', '--app=quality-gates'] } { grunt: true, args: ['requirejs:app', '--app=quality-profiles'] } { grunt: true, args: ['requirejs:app', '--app=source-viewer'] } { grunt: true, args: ['requirejs:app', '--app=users'] } @@ -195,8 +195,8 @@ module.exports = (grunt) -> '<%= SOURCE_PATH %>/js/components/common/templates/**/*.hbs' '<%= SOURCE_PATH %>/js/apps/coding-rules/templates/**/*.hbs' ] - '<%= BUILD_PATH %>/js/apps/quality-gate/templates.js': [ - '<%= SOURCE_PATH %>/coffee/apps/quality-gate/templates/**/*.hbs' + '<%= BUILD_PATH %>/js/apps/quality-gates/templates.js': [ + '<%= SOURCE_PATH %>/js/apps/quality-gates/templates/**/*.hbs' ] '<%= BUILD_PATH %>/js/apps/quality-profiles/templates.js': [ '<%= SOURCE_PATH %>/js/apps/quality-profiles/templates/**/*.hbs' 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 @@ - - {{metric.name}} - {{#if metric.hidden}} - {{t 'deprecated'}} - {{/if}} - - - {{#if canEdit}} - - {{else}} - {{#if periodText}}Δ {{periodText}} - {{else}}{{t 'value'}} - {{/if}} - {{/if}} - - - {{#if canEdit}} - - {{else}} - {{t 'quality_gates.operator' op}} - {{/if}} - - - - {{#if canEdit}} - - {{else}} - {{warning}} - {{/if}} - - - - {{#if canEdit}} - - {{else}} - {{error}} - {{/if}} - - - {{#if canEdit}} - {{#if id}} -
- - -
- {{else}} -
- - {{t 'cancel'}} -
- {{/if}} - {{/if}} - 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 @@ - - {{t 'quality_gates.no_conditions'}} - \ 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 @@ -
{{t 'quality_gates.conditions'}}
- -
-

{{t 'quality_gates.introduction'}} - {{t 'more'}} -

-
- {{t 'quality_gates.health_icons'}} -
    -
  • - - {{t 'alerts.notes.ok'}} -
  • -
  • - - {{t 'alerts.notes.warn'}} -
  • -
  • - - {{t 'alerts.notes.error'}} -
  • -
-
-
- -{{#if canEdit}} -
- - -
-{{/if}} - -
- - - -
-
\ 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 @@ -
- - - - - -
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 @@ -
{{t 'quality_gates.noQualityGates'}}
\ 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: '' - - - 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('').data('type', @model.get('metric').type).val(@model.get('warning')).originalVal() - error: jQuery('').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: '' - - - 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 similarity index 100% rename from server/sonar-web/src/main/coffee/apps/quality-gate/templates/_quality-gate-intro.hbs rename to 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 similarity index 100% rename from server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-actions.hbs rename to 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 @@ + + {{metric.name}} + {{#if metric.hidden}} + {{t 'deprecated'}} + {{/if}} + + + + {{#if canEdit}} + + {{else}} + Δ {{periodText}} + {{/if}} + + + + {{#if canEdit}} + + {{else}} + {{t 'quality_gates.operator' op}} + {{/if}} + + + + + {{#if canEdit}} + + {{else}} + {{formatMeasure warning metric.type}} + {{/if}} + + + + + {{#if canEdit}} + + {{else}} + {{formatMeasure error metric.type}} + {{/if}} + + +{{#if canEdit}} + + {{#if id}} +
+ + +
+ {{else}} +
+ + {{t 'cancel'}} +
+ {{/if}} + +{{/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 @@ + + {{t 'quality_gates.no_conditions'}} + 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 @@ +

{{t 'quality_gates.conditions'}}

+ +
+
+ {{t 'quality_gates.introduction'}} + {{t 'more'}}  +
+ +
+ +{{#if canEdit}} +
+ + +
+{{/if}} + +
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 similarity index 64% rename from server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-header.hbs rename to 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 @@
- {{#if default}} - - {{else}} - - {{/if}} +
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 similarity index 92% rename from server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail-projects.hbs rename to 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 @@
{{t 'quality_gates.projects'}}
-{{#if default}} +{{#if isDefault}}

{{#if canEdit}} {{t 'quality_gates.projects_for_default.edit'}} @@ -10,4 +10,4 @@

{{else}}
-{{/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 similarity index 100% rename from server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-detail.hbs rename to 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 @@ +
+ + + +
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 @@ +
+ + + +
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 @@ +
+ + + +
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 similarity index 89% rename from server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gate-sidebar-list-item.hbs rename to 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 @@ {{name}} - {{#if default}} + {{#if isDefault}} {{t 'default'}} {{/if}} 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 @@ +
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 similarity index 100% rename from server/sonar-web/src/main/coffee/apps/quality-gate/templates/quality-gates-layout.hbs rename to 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 %> - -<% end %> - -
+
+ 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,14 +104,20 @@ 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 -- 2.39.5