From 20499b26a45114913f94387be92e9c473c5c7170 Mon Sep 17 00:00:00 2001 From: Stas Vilchik Date: Fri, 17 Apr 2015 17:21:04 +0200 Subject: [PATCH] SONAR-6210 add a rule description in the workspace --- .../src/main/coffee/issue/issue-view.coffee | 14 ++--- .../coffee/issue/views/rule-overlay.coffee | 35 ------------ .../src/main/hbs/workspace/workspace-item.hbs | 2 +- .../workspace-rule.hbs} | 17 ++---- .../hbs/workspace/workspace-viewer-header.hbs | 2 +- .../main/js/common/handlebars-extensions.js | 6 +- .../sonar-web/src/main/js/workspace/main.js | 39 +++++++++---- .../js/workspace/views/base-viewer-view.js | 57 +++++++++++++++++++ .../src/main/js/workspace/views/rule-view.js | 40 +++++++++++++ .../main/js/workspace/views/viewer-view.js | 34 ++--------- .../sonar-web/src/test/js/issues-page-spec.js | 42 ++++++++++++++ server/sonar-web/src/test/js/workspace.js | 43 ++++++++++++++ .../src/test/json/issues-spec/rule.json | 36 ++++++++++++ .../src/test/json/workspace/rule.json | 36 ++++++++++++ 14 files changed, 305 insertions(+), 98 deletions(-) delete mode 100644 server/sonar-web/src/main/coffee/issue/views/rule-overlay.coffee rename server/sonar-web/src/main/hbs/{issue/issue-rule.hbs => workspace/workspace-rule.hbs} (64%) create mode 100644 server/sonar-web/src/main/js/workspace/views/base-viewer-view.js create mode 100644 server/sonar-web/src/main/js/workspace/views/rule-view.js create mode 100644 server/sonar-web/src/test/json/issues-spec/rule.json create mode 100644 server/sonar-web/src/test/json/workspace/rule.json diff --git a/server/sonar-web/src/main/coffee/issue/issue-view.coffee b/server/sonar-web/src/main/coffee/issue/issue-view.coffee index 457f0cffd59..bb2ea79d86b 100644 --- a/server/sonar-web/src/main/coffee/issue/issue-view.coffee +++ b/server/sonar-web/src/main/coffee/issue/issue-view.coffee @@ -32,9 +32,10 @@ define [ 'issue/views/plan-form-view' 'issue/views/set-severity-form-view' 'issue/views/more-actions-view' - 'issue/views/rule-overlay' 'issue/views/tags-form-view' + 'workspace/main' + 'templates/issue' ], ( @@ -51,9 +52,10 @@ define [ PlanFormView SetSeverityFormView MoreActionsView - RuleOverlay TagsFormView + Workspace + ) -> $ = jQuery @@ -261,12 +263,10 @@ define [ showRule: -> + unless Workspace? + Workspace = require 'workspace/main' ruleKey = @model.get 'rule' - $.get "#{baseUrl}/api/rules/show", key: ruleKey, (r) => - ruleOverlay = new RuleOverlay - model: new Backbone.Model r.rule - large: true - ruleOverlay.render() + Workspace.openRule key: ruleKey editTags: (e)-> diff --git a/server/sonar-web/src/main/coffee/issue/views/rule-overlay.coffee b/server/sonar-web/src/main/coffee/issue/views/rule-overlay.coffee deleted file mode 100644 index c4dbc5a2fb3..00000000000 --- a/server/sonar-web/src/main/coffee/issue/views/rule-overlay.coffee +++ /dev/null @@ -1,35 +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 [ - 'common/modals' - 'templates/issue' -], ( - ModalView -) -> - - class extends ModalView - template: Templates['issue-rule'] - - - serializeData: -> - _.extend super, - permalink: "#{baseUrl}/coding_rules#rule_key=#{encodeURIComponent @model.get('key')}" - allTags: _.union @model.get('sysTags'), @model.get('tags') diff --git a/server/sonar-web/src/main/hbs/workspace/workspace-item.hbs b/server/sonar-web/src/main/hbs/workspace/workspace-item.hbs index 24cf2862541..a4fb7a98661 100644 --- a/server/sonar-web/src/main/hbs/workspace/workspace-item.hbs +++ b/server/sonar-web/src/main/hbs/workspace/workspace-item.hbs @@ -2,6 +2,6 @@ {{qualifierIcon q}} {{/if}} -{{default name uuid}} +{{limitString name}} diff --git a/server/sonar-web/src/main/hbs/issue/issue-rule.hbs b/server/sonar-web/src/main/hbs/workspace/workspace-rule.hbs similarity index 64% rename from server/sonar-web/src/main/hbs/issue/issue-rule.hbs rename to server/sonar-web/src/main/hbs/workspace/workspace-rule.hbs index 2e11a623e9d..acdf14906c2 100644 --- a/server/sonar-web/src/main/hbs/issue/issue-rule.hbs +++ b/server/sonar-web/src/main/hbs/workspace/workspace-rule.hbs @@ -1,12 +1,6 @@ - - - diff --git a/server/sonar-web/src/main/hbs/workspace/workspace-viewer-header.hbs b/server/sonar-web/src/main/hbs/workspace/workspace-viewer-header.hbs index c6546fa7fd6..ef4971f6077 100644 --- a/server/sonar-web/src/main/hbs/workspace/workspace-viewer-header.hbs +++ b/server/sonar-web/src/main/hbs/workspace/workspace-viewer-header.hbs @@ -1,4 +1,4 @@ -
{{qualifierIcon q}} {{name}}
+
{{#if q}}{{qualifierIcon q}} {{/if}}{{name}}
diff --git a/server/sonar-web/src/main/js/common/handlebars-extensions.js b/server/sonar-web/src/main/js/common/handlebars-extensions.js index 158cf1ac898..83826c5de35 100644 --- a/server/sonar-web/src/main/js/common/handlebars-extensions.js +++ b/server/sonar-web/src/main/js/common/handlebars-extensions.js @@ -517,8 +517,10 @@ }); Handlebars.registerHelper('limitString', function (str) { - var LIMIT = 30; - return str.length > LIMIT ? str.substr(0, LIMIT) + '...' : str; + if (typeof str === 'string') { + var LIMIT = 30; + return str.length > LIMIT ? str.substr(0, LIMIT) + '...' : str; + } }); Handlebars.registerHelper('withSign', function (number) { diff --git a/server/sonar-web/src/main/js/workspace/main.js b/server/sonar-web/src/main/js/workspace/main.js index a68efff25ba..594f8e783c2 100644 --- a/server/sonar-web/src/main/js/workspace/main.js +++ b/server/sonar-web/src/main/js/workspace/main.js @@ -21,8 +21,9 @@ define([ 'workspace/models/item', 'workspace/models/items', 'workspace/views/items-view', - 'workspace/views/viewer-view' -], function (Item, Items, ItemsView, ViewerView) { + 'workspace/views/viewer-view', + 'workspace/views/rule-view' +], function (Item, Items, ItemsView, ViewerView, RuleView) { var $ = jQuery, @@ -56,10 +57,6 @@ define([ this.items.save(); }, - load: function () { - this.items.load(); - }, - addComponent: function (model) { if (!this.items.has(model)) { this.items.add(model); @@ -76,21 +73,26 @@ define([ if (model.isComponent()) { this.showComponentViewer(model); } + if (model.isRule()) { + this.showRule(model); + } }, openComponent: function (options) { return this.open(_.extend(options, { type: 'component' })); }, - showComponentViewer: function (model) { + openRule: function (options) { + return this.open(_.extend(options, { type: 'rule' })); + }, + + showViewer: function (Viewer, model) { var that = this; if (this.viewerView != null) { this.viewerView.close(); } $('.source-viewer').addClass('with-workspace'); - this.viewerView = new ViewerView({ - model: model - }); + this.viewerView = new Viewer({ model: model }); this.viewerView .on('viewerMinimize', function () { that.closeComponentViewer(); @@ -102,11 +104,28 @@ define([ this.viewerView.render().$el.appendTo(document.body); }, + showComponentViewer: function (model) { + this.showViewer(ViewerView, model); + }, + closeComponentViewer: function () { if (this.viewerView != null) { this.viewerView.close(); $('.with-workspace').removeClass('with-workspace'); } + }, + + showRule: function (model) { + this.showViewer(RuleView, model); + this.fetchRule(model); + }, + + fetchRule: function (model) { + var url = baseUrl + '/api/rules/show', + options = { key: model.get('key') }; + return $.get(url, options).done(function (r) { + model.set(r.rule); + }); } }; diff --git a/server/sonar-web/src/main/js/workspace/views/base-viewer-view.js b/server/sonar-web/src/main/js/workspace/views/base-viewer-view.js new file mode 100644 index 00000000000..20396a7cb51 --- /dev/null +++ b/server/sonar-web/src/main/js/workspace/views/base-viewer-view.js @@ -0,0 +1,57 @@ +/* + * 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([ + 'workspace/views/viewer-header-view' +], function (HeaderView) { + + return Marionette.Layout.extend({ + className: 'workspace-viewer', + + modelEvents: { + 'destroy': 'close' + }, + + regions: { + headerRegion: '.workspace-viewer-header', + viewerRegion: '.workspace-viewer-container' + }, + + onRender: function () { + this.showHeader(); + this.$('.workspace-viewer-container').isolatedScroll(); + }, + + onViewerMinimize: function () { + this.trigger('viewerMinimize'); + }, + + onViewerClose: function () { + this.trigger('viewerClose', this.model); + }, + + showHeader: function () { + var headerView = new HeaderView({ model: this.model }); + this.listenTo(headerView, 'viewerMinimize', this.onViewerMinimize); + this.listenTo(headerView, 'viewerClose', this.onViewerClose); + this.headerRegion.show(headerView); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/workspace/views/rule-view.js b/server/sonar-web/src/main/js/workspace/views/rule-view.js new file mode 100644 index 00000000000..f3626009209 --- /dev/null +++ b/server/sonar-web/src/main/js/workspace/views/rule-view.js @@ -0,0 +1,40 @@ +/* + * 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([ + 'workspace/views/base-viewer-view', + 'templates/workspace' +], function (BaseView) { + + return BaseView.extend({ + template: Templates['workspace-rule'], + + modelEvents: { + 'destroy': 'close', + 'change': 'render' + }, + + serializeData: function () { + return _.extend(Marionette.Layout.prototype.serializeData.apply(this, arguments), { + allTags: _.union(this.model.get('sysTags'), this.model.get('tags')) + }); + } + }); + +}); diff --git a/server/sonar-web/src/main/js/workspace/views/viewer-view.js b/server/sonar-web/src/main/js/workspace/views/viewer-view.js index d294a56a83c..171186f6179 100644 --- a/server/sonar-web/src/main/js/workspace/views/viewer-view.js +++ b/server/sonar-web/src/main/js/workspace/views/viewer-view.js @@ -18,43 +18,17 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ define([ - 'workspace/views/viewer-header-view', + 'workspace/views/base-viewer-view', 'source-viewer/viewer', 'templates/workspace' -], function (HeaderView, SourceViewer) { +], function (BaseView, SourceViewer) { - return Marionette.Layout.extend({ - className: 'workspace-viewer', + return BaseView.extend({ template: Templates['workspace-viewer'], - modelEvents: { - 'destroy': 'close' - }, - - regions: { - headerRegion: '.workspace-viewer-header', - viewerRegion: '.workspace-viewer-container' - }, - onRender: function () { - this.showHeader(); + BaseView.prototype.onRender.apply(this, arguments); this.showViewer(); - this.$('.workspace-viewer-container').isolatedScroll(); - }, - - onViewerMinimize: function () { - this.trigger('viewerMinimize'); - }, - - onViewerClose: function () { - this.trigger('viewerClose', this.model); - }, - - showHeader: function () { - var headerView = new HeaderView({ model: this.model }); - this.listenTo(headerView, 'viewerMinimize', this.onViewerMinimize); - this.listenTo(headerView, 'viewerClose', this.onViewerClose); - this.headerRegion.show(headerView); }, showViewer: function () { diff --git a/server/sonar-web/src/test/js/issues-page-spec.js b/server/sonar-web/src/test/js/issues-page-spec.js index aadd4db0ae1..89286655883 100644 --- a/server/sonar-web/src/test/js/issues-page-spec.js +++ b/server/sonar-web/src/test/js/issues-page-spec.js @@ -234,6 +234,48 @@ casper.test.begin(testName('Issue Box', 'Transitions'), function (test) { }); +casper.test.begin(testName('Issue Box', 'Rule'), function (test) { + casper + .start(lib.buildUrl('issues'), function () { + lib.setDefaultViewport(); + + + lib.mockRequestFromFile('/api/issue_filters/app', 'app.json'); + lib.mockRequestFromFile('/api/issues/search', 'search.json'); + lib.mockRequestFromFile('/api/rules/show', 'rule.json'); + }) + + .then(function () { + casper.evaluate(function () { + require(['/js/issues/app-new.js']); + }); + }) + + .then(function () { + casper.waitForSelector('.issue.selected'); + }) + + .then(function () { + casper.click('.issue.selected .js-issue-rule'); + casper.waitForSelector('.workspace-viewer-container .coding-rules-detail-properties'); + }) + + .then(function () { + test.assertSelectorContains('.workspace-viewer-name', 'Insufficient branch coverage by unit tests'); + test.assertSelectorContains('.workspace-viewer-container', 'Reliability > Unit tests coverage'); + test.assertSelectorContains('.workspace-viewer-container', 'An issue is created on a file as soon as the'); + }) + + .then(function () { + lib.sendCoverage(); + }) + + .run(function () { + test.done(); + }); +}); + + casper.test.begin(testName('File-Level Issues'), function (test) { var issueKey = '200d4a8b-9666-4e70-9953-7bab57933f97', issueSelector = '.issue[data-key="' + issueKey + '"]'; diff --git a/server/sonar-web/src/test/js/workspace.js b/server/sonar-web/src/test/js/workspace.js index 35a9ef58026..47ce6bd3839 100644 --- a/server/sonar-web/src/test/js/workspace.js +++ b/server/sonar-web/src/test/js/workspace.js @@ -286,3 +286,46 @@ casper.test.begin(testName('Full Screen'), 6, function (test) { test.done(); }); }); + + +casper.test.begin(testName('Rule'), 3, function (test) { + casper + .start(lib.buildUrl('nav'), function () { + lib.setDefaultViewport(); + + lib.mockRequestFromFile('/api/rules/show', 'rule.json'); + }) + + .then(function () { + casper.evaluate(function () { + window.localStorage.setItem('sonarqube-workspace', + '[{"key":"common-java:InsufficientBranchCoverage","type":"rule"}]'); + window.SS.isUserAdmin = false; + window.navbarOptions = new Backbone.Model(); + require(['/js/nav/app.js']); + }); + }) + + .then(function () { + casper.waitForSelector('.workspace-nav-item'); + }) + + .then(function () { + casper.click('.workspace-nav-item'); + casper.waitForSelector('.workspace-viewer .coding-rules-detail-properties'); + }) + + .then(function () { + test.assertSelectorContains('.workspace-viewer-name', 'Insufficient branch coverage by unit tests'); + test.assertSelectorContains('.workspace-viewer-container', 'Reliability > Unit tests coverage'); + test.assertSelectorContains('.workspace-viewer-container', 'An issue is created on a file as soon as the'); + }) + + .then(function () { + lib.sendCoverage(); + }) + + .run(function () { + test.done(); + }); +}); diff --git a/server/sonar-web/src/test/json/issues-spec/rule.json b/server/sonar-web/src/test/json/issues-spec/rule.json new file mode 100644 index 00000000000..f4abb0ce469 --- /dev/null +++ b/server/sonar-web/src/test/json/issues-spec/rule.json @@ -0,0 +1,36 @@ +{ + "rule": { + "key": "common-java:InsufficientBranchCoverage", + "repo": "common-java", + "name": "Insufficient branch coverage by unit tests", + "createdAt": "2015-04-13T13:44:07+0200", + "severity": "MAJOR", + "status": "READY", + "internalKey": "InsufficientBranchCoverage", + "isTemplate": false, + "tags": [], + "sysTags": [], + "lang": "java", + "langName": "Java", + "htmlDesc": "

An issue is created on a file as soon as the branch coverage on this file is less than the required threshold.It gives the number of branches to be covered in order to reach the required threshold.

", + "defaultDebtChar": "RELIABILITY", + "defaultDebtSubChar": "UNIT_TESTS", + "debtChar": "RELIABILITY", + "debtSubChar": "UNIT_TESTS", + "debtCharName": "Reliability", + "debtSubCharName": "Unit tests coverage", + "defaultDebtRemFnType": "LINEAR", + "defaultDebtRemFnCoeff": "10min", + "debtOverloaded": false, + "debtRemFnType": "LINEAR", + "debtRemFnCoeff": "10min", + "params": [ + { + "key": "minimumBranchCoverageRatio", + "htmlDesc": "The minimum required branch coverage ratio.", + "type": "STRING", + "defaultValue": "65.0" + } + ] + } +} diff --git a/server/sonar-web/src/test/json/workspace/rule.json b/server/sonar-web/src/test/json/workspace/rule.json new file mode 100644 index 00000000000..f4abb0ce469 --- /dev/null +++ b/server/sonar-web/src/test/json/workspace/rule.json @@ -0,0 +1,36 @@ +{ + "rule": { + "key": "common-java:InsufficientBranchCoverage", + "repo": "common-java", + "name": "Insufficient branch coverage by unit tests", + "createdAt": "2015-04-13T13:44:07+0200", + "severity": "MAJOR", + "status": "READY", + "internalKey": "InsufficientBranchCoverage", + "isTemplate": false, + "tags": [], + "sysTags": [], + "lang": "java", + "langName": "Java", + "htmlDesc": "

An issue is created on a file as soon as the branch coverage on this file is less than the required threshold.It gives the number of branches to be covered in order to reach the required threshold.

", + "defaultDebtChar": "RELIABILITY", + "defaultDebtSubChar": "UNIT_TESTS", + "debtChar": "RELIABILITY", + "debtSubChar": "UNIT_TESTS", + "debtCharName": "Reliability", + "debtSubCharName": "Unit tests coverage", + "defaultDebtRemFnType": "LINEAR", + "defaultDebtRemFnCoeff": "10min", + "debtOverloaded": false, + "debtRemFnType": "LINEAR", + "debtRemFnCoeff": "10min", + "params": [ + { + "key": "minimumBranchCoverageRatio", + "htmlDesc": "The minimum required branch coverage ratio.", + "type": "STRING", + "defaultValue": "65.0" + } + ] + } +} -- 2.39.5