]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-5820 On "template" rules, make it possible to create "custom" rules
authorStas Vilchik <vilchiks@gmail.com>
Fri, 26 Dec 2014 13:43:40 +0000 (14:43 +0100)
committerStas Vilchik <vilchiks@gmail.com>
Fri, 26 Dec 2014 13:43:40 +0000 (14:43 +0100)
12 files changed:
server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-custom-rule-creation.hbs [new file with mode: 0644]
server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-custom-rule.hbs
server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-custom-rules.hbs
server/sonar-web/src/main/js/coding-rules/controller.js
server/sonar-web/src/main/js/coding-rules/rule/custom-rule-creation-view.js [new file with mode: 0644]
server/sonar-web/src/main/js/coding-rules/rule/custom-rules-view.js
server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-custom-rules.js [new file with mode: 0644]
server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-custom-rules/app.json [new file with mode: 0644]
server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-custom-rules/search-custom-rules.json [new file with mode: 0644]
server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-custom-rules/search-custom-rules2.json [new file with mode: 0644]
server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-custom-rules/search.json [new file with mode: 0644]
server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-custom-rules/show.json [new file with mode: 0644]

diff --git a/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-custom-rule-creation.hbs b/server/sonar-web/src/main/hbs/coding-rules/rule/coding-rules-custom-rule-creation.hbs
new file mode 100644 (file)
index 0000000..2463840
--- /dev/null
@@ -0,0 +1,86 @@
+<form>
+  <div class="modal-head">
+    {{#if change}}
+      <h2>{{t 'coding_rules.update_custom_rule'}}</h2>
+    {{else}}
+      <h2>{{t 'coding_rules.create_custom_rule'}}</h2>
+    {{/if}}
+  </div>
+
+  <div class="modal-body">
+    <div class="modal-error"></div>
+    <div class="modal-warning">{{t 'coding_rules.reactivate.help'}}</div>
+
+    <table>
+      <tr class="property">
+        <th><h3>{{t 'name'}} <em class="mandatory">*</em></h3></th>
+        <td>
+          <input type="text" name="name" id="coding-rules-custom-rule-creation-name"
+            class="coding-rules-name-key" value="{{name}}"/>
+        </td>
+      </tr>
+      <tr class="property">
+        <th><h3>{{t 'key'}}{{#unless change}} <em class="mandatory">*</em>{{/unless}}</h3></th>
+        <td>
+          {{#if change}}
+            <span class="coding-rules-detail-custom-rule-key" title="{{key}}">{{key}}</span>
+          {{else}}
+            <input type="text" name="key" id="coding-rules-custom-rule-creation-key"
+              class="coding-rules-name-key" value="{{internalKey}}"/>
+          {{/if}}
+        </td>
+      </tr>
+      <tr class="property">
+        <th><h3>{{t 'description'}} <em class="mandatory">*</em></h3></th>
+        <td>
+          <textarea name="markdown_description" id="coding-rules-custom-rule-creation-html-description"
+            class="coding-rules-markdown-description" rows="15">{{{mdDesc}}}</textarea>
+          <span class="right">{{> '_markdown-tips' }}</span>
+        </td>
+      </tr>
+      <tr class="property">
+        <th><h3>{{t 'severity'}}</h3></th>
+        <td>
+          <select id="coding-rules-custom-rule-creation-severity">
+            {{#each severities}}
+              <option value="{{this}}">{{t 'severity' this}}</option>
+            {{/each}}
+          </select>
+        </td>
+      </tr>
+      <tr class="property">
+        <th><h3>{{t 'coding_rules.filters.status'}}</h3></th>
+        <td>
+          <select id="coding-rules-custom-rule-creation-status">
+            {{#each statuses}}
+              <option value="{{id}}">{{text}}</option>
+            {{/each}}
+          </select>
+        </td>
+      </tr>
+      {{#each params}}
+        <tr class="property">
+          <th><h3>{{key}}</h3></th>
+          <td>
+            {{#eq type 'TEXT'}}
+              <textarea class="width100" rows="3" name="{{key}}" placeholder="{{defaultValue}}">{{value}}</textarea>
+            {{else}}
+              <input type="text" name="{{key}}" value="{{value}}" placeholder="{{defaultValue}}"/>
+            {{/eq}}
+            <div class="note">{{htmlDesc}}</div>
+            {{#if extra}}
+              <div class="note">{{extra}}</div>
+            {{/if}}
+          </td>
+        </tr>
+      {{/each}}
+    </table>
+  </div>
+
+  <div class="modal-foot">
+    <button id="coding-rules-custom-rule-creation-create">
+      {{#if change}}{{t 'save'}}{{else}}{{t 'create'}}{{/if}}
+    </button>
+    <a id="coding-rules-custom-rule-creation-cancel" class="action">{{t 'cancel'}}</a>
+  </div>
+</form>
index 4d8f07efd371b0996cb797184f8e876df8649956..94f72b03f3515a23916921822fe6702f857b890b 100644 (file)
@@ -7,10 +7,12 @@
 </td>
 
 <td class="coding-rules-detail-list-parameters">
-  {{#each parameters}}
-    <div class="coding-rules-detail-list-parameter">
-      <span class="key">{{key}}</span><span class="sep">:&nbsp;</span><span class="value" title="{{value}}">{{value}}</span>
-    </div>
+  {{#each params}}
+    {{#if defaultValue}}
+      <div class="coding-rules-detail-list-parameter">
+        <span class="key">{{key}}</span><span class="sep">:&nbsp;</span><span class="value" title="{{value}}">{{defaultValue}}</span>
+      </div>
+    {{/if}}
   {{/each}}
   &nbsp;
 </td>
index f2a2cc9793e4180344aaca8d2ff361d1e28d5b14..4e03b21754d729f95ad484a111bc262ac0b8ef65 100644 (file)
@@ -3,7 +3,7 @@
 
   {{#if canWrite}}
     <div class="button-group coding-rules-detail-quality-profiles-activation">
-      <button id="coding-rules-custom-rules-create">{{t 'coding_rules.create'}}</button>
+      <button class="js-create-custom-rule">{{t 'coding_rules.create'}}</button>
     </div>
   {{/if}}
   <table id="coding-rules-detail-custom-rules" class="coding-rules-detail-list"></table>
index c731ee16c43b794e863b86cb186ecfeeba601b38..b1c1c4986491b9244848b199d1b29f7653f95bd0 100644 (file)
@@ -1,7 +1,8 @@
 define([
     'components/navigator/controller',
+    'coding-rules/models/rule',
     'coding-rules/rule-details-view'
-], function (Controller, RuleDetailsView) {
+], function (Controller, Rule, RuleDetailsView) {
 
   var $ = jQuery;
 
@@ -92,7 +93,7 @@ define([
 
     showDetails: function (rule) {
       var that = this,
-          ruleModel = typeof rule === 'string' ? new Backbone.Model({ key: rule }) : rule;
+          ruleModel = typeof rule === 'string' ? new Rule({ key: rule }) : rule;
       this.app.layout.workspaceDetailsRegion.reset();
       this.getRuleDetails(ruleModel).done(function (data) {
         key.setScope('details');
diff --git a/server/sonar-web/src/main/js/coding-rules/rule/custom-rule-creation-view.js b/server/sonar-web/src/main/js/coding-rules/rule/custom-rule-creation-view.js
new file mode 100644 (file)
index 0000000..0e8f6a8
--- /dev/null
@@ -0,0 +1,183 @@
+define([
+  'common/modals',
+  'templates/coding-rules'
+], function (Modal, Templates) {
+
+  var $ = jQuery;
+
+  return Modal.extend({
+    template: Templates['coding-rules-custom-rule-creation'],
+
+    ui: {
+      customRuleCreationKey: '#coding-rules-custom-rule-creation-key',
+      customRuleCreationName: '#coding-rules-custom-rule-creation-name',
+      customRuleCreationHtmlDescription: '#coding-rules-custom-rule-creation-html-description',
+      customRuleCreationSeverity: '#coding-rules-custom-rule-creation-severity',
+      customRuleCreationStatus: '#coding-rules-custom-rule-creation-status',
+      customRuleCreationParameters: '[name]',
+      customRuleCreationCreate: '#coding-rules-custom-rule-creation-create',
+      customRuleCreationReactivate: '#coding-rules-custom-rule-creation-reactivate',
+      modalFoot: '.modal-foot'
+    },
+
+    events: function () {
+      return _.extend(Modal.prototype.events.apply(this, arguments), {
+        'input @ui.customRuleCreationName': 'generateKey',
+        'keydown @ui.customRuleCreationName': 'generateKey',
+        'keyup @ui.customRuleCreationName': 'generateKey',
+
+        'input @ui.customRuleCreationKey': 'flagKey',
+        'keydown @ui.customRuleCreationKey': 'flagKey',
+        'keyup @ui.customRuleCreationKey': 'flagKey',
+
+        'click #coding-rules-custom-rule-creation-cancel': 'close',
+        'click @ui.customRuleCreationCreate': 'create',
+        'click @ui.customRuleCreationReactivate': 'reactivate'
+      });
+    },
+
+    generateKey: function () {
+      if (!this.keyModifiedByUser) {
+        if (this.ui.customRuleCreationKey) {
+          var generatedKey = this.ui.customRuleCreationName.val().latinize().replace(/[^A-Za-z0-9]/g, '_');
+          this.ui.customRuleCreationKey.val(generatedKey);
+        }
+      }
+    },
+
+    flagKey: function () {
+      this.keyModifiedByUser = true;
+      // Cannot use @ui.customRuleCreationReactivate.hide() directly since it was not there at initial render
+      jQuery(this.ui.customRuleCreationReactivate.selector).hide();
+    },
+
+    onRender: function () {
+      Modal.prototype.onRender.apply(this, arguments);
+
+      this.keyModifiedByUser = false;
+
+      var format = function (state) {
+            if (!state.id) {
+              return state.text;
+            } else {
+              return '<i class="icon-severity-' + state.id.toLowerCase() + '"></i> ' + state.text;
+            }
+          },
+          severity = (this.model && this.model.get('severity')) || this.options.templateRule.get('severity'),
+          status = (this.model && this.model.get('status')) || this.options.templateRule.get('status');
+
+      this.ui.customRuleCreationSeverity.val(severity);
+      this.ui.customRuleCreationSeverity.select2({
+        width: '250px',
+        minimumResultsForSearch: 999,
+        formatResult: format,
+        formatSelection: format
+      });
+
+      this.ui.customRuleCreationStatus.val(status);
+      this.ui.customRuleCreationStatus.select2({
+        width: '250px',
+        minimumResultsForSearch: 999
+      });
+    },
+
+    create: function (e) {
+      e.preventDefault();
+      var action = (this.model && this.model.has('key')) ? 'update' : 'create',
+          options = {
+            name: this.ui.customRuleCreationName.val(),
+            markdown_description: this.ui.customRuleCreationHtmlDescription.val(),
+            severity: this.ui.customRuleCreationSeverity.val(),
+            status: this.ui.customRuleCreationStatus.val()
+          };
+      if (this.model && this.model.has('key')) {
+        options.key = this.model.get('key');
+      } else {
+        _.extend(options, {
+          template_key: this.options.templateRule.get('key'),
+          custom_key: this.ui.customRuleCreationKey.val(),
+          prevent_reactivation: true
+        });
+      }
+      var params = this.ui.customRuleCreationParameters.map(function () {
+        var node = $(this),
+            value = node.val();
+        if (!value && action === 'create') {
+          value = node.prop('placeholder') || '';
+        }
+        return {
+          key: node.prop('name'),
+          value: value
+        };
+      }).get();
+      options.params = params.map(function (param) {
+        return param.key + '=' + window.csvEscape(param.value);
+      }).join(';');
+      this.sendRequest(action, options);
+    },
+
+    reactivate: function () {
+      var options = {
+            name: this.existingRule.name,
+            markdown_description: this.existingRule.mdDesc,
+            severity: this.existingRule.severity,
+            status: this.existingRule.status,
+            template_key: this.existingRule.templateKey,
+            custom_key: this.ui.customRuleCreationKey.val(),
+            prevent_reactivation: false
+          },
+          params = this.existingRule.params;
+      options.params = params.map(function (param) {
+        return param.key + '=' + param.defaultValue;
+      }).join(';');
+      this.sendRequest('create', options);
+    },
+
+    sendRequest: function (action, options) {
+      this.$('.modal-error').hide();
+      this.$('.modal-warning').hide();
+      var that = this,
+          origFooter = this.ui.modalFoot.html(),
+          url = baseUrl + '/api/rules/' + action;
+      return $.post(url, options).done(function () {
+        that.options.app.controller.showDetails(that.options.templateRule);
+        that.close();
+      }).fail(function (jqXHR, textStatus, errorThrown) {
+        if (jqXHR.status === 409) {
+          that.existingRule = jqXHR.responseJSON.rule;
+          that.$('.modal-warning').show();
+          that.ui.modalFoot.html(Templates['coding-rules-custom-rule-reactivation']());
+        } else {
+          jQuery.ajaxSettings.error(jqXHR, textStatus, errorThrown);
+          that.ui.modalFoot.html(origFooter);
+        }
+      });
+    },
+
+    serializeData: function () {
+      var params = {};
+      if (this.options.templateRule) {
+        params = this.options.templateRule.get('params');
+      } else if (this.model && this.model.has('params')) {
+        params = this.model.get('params').map(function (p) {
+          _.extend(p, { value: p.defaultValue });
+        });
+      }
+
+      var statuses = ['READY', 'BETA', 'DEPRECATED'].map(function (status) {
+        return {
+          id: status,
+          text: t('rules.status', status.toLowerCase())
+        };
+      });
+
+      return _.extend(Modal.prototype.serializeData.apply(this, arguments), {
+        change: this.model && this.model.has('key'),
+        params: params,
+        severities: ['BLOCKER', 'CRITICAL', 'MAJOR', 'MINOR', 'INFO'],
+        statuses: statuses
+      });
+    }
+  });
+
+});
index a6a25c7982da5145346d8170367decf2411a1cdd..ea519ab4b51bbca4cf3ab03e9bf9689fd6062c7c 100644 (file)
@@ -1,8 +1,9 @@
 define([
   'backbone.marionette',
   'templates/coding-rules',
-  'coding-rules/rule/custom-rule-view'
-], function (Marionette, Templates, CustomRuleView) {
+  'coding-rules/rule/custom-rule-view',
+  'coding-rules/rule/custom-rule-creation-view'
+], function (Marionette, Templates, CustomRuleView, CustomRuleCreationView) {
 
   return Marionette.CompositeView.extend({
     template: Templates['coding-rules-custom-rules'],
@@ -20,10 +21,21 @@ define([
       'change': 'render'
     },
 
+    events: {
+      'click .js-create-custom-rule': 'createCustomRule'
+    },
+
     onRender: function () {
       this.$el.toggleClass('hidden', !this.model.get('isTemplate'));
     },
 
+    createCustomRule: function () {
+      new CustomRuleCreationView({
+        app: this.options.app,
+        templateRule: this.model
+      }).render();
+    },
+
     serializeData: function () {
       return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
         canWrite: this.options.app.canWrite
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-custom-rules.js b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-custom-rules.js
new file mode 100644 (file)
index 0000000..761a0d1
--- /dev/null
@@ -0,0 +1,55 @@
+/* global casper:false */
+
+var lib = require('../lib');
+
+lib.initMessages();
+lib.changeWorkingDirectory('coding-rules-page-should-create-custom-rules');
+
+
+casper.test.begin('coding-rules-page-should-delete-create-rules', 2, function (test) {
+  casper
+      .start(lib.buildUrl('coding-rules'), function () {
+        lib.setDefaultViewport();
+
+        lib.mockRequest('/api/l10n/index', '{}');
+        lib.mockRequestFromFile('/api/rules/app', 'app.json');
+        this.customRulesSearchMock = lib.mockRequestFromFile('/api/rules/search', 'search-custom-rules.json',
+            { data: { template_key: 'squid:ArchitecturalConstraint' } });
+        this.searchMock = lib.mockRequestFromFile('/api/rules/search', 'search.json');
+        lib.mockRequestFromFile('/api/rules/show', 'show.json');
+        lib.mockRequest('/api/rules/create', '{}');
+      })
+
+      .then(function () {
+        casper.waitForSelector('.coding-rule.selected', function () {
+          casper.click('.coding-rule.selected .js-rule');
+        });
+      })
+
+      .then(function () {
+        casper.waitForSelector('#coding-rules-detail-custom-rules .coding-rules-detail-list-name');
+      })
+
+      .then(function () {
+        lib.clearRequestMock(this.customRulesSearchMock);
+        lib.clearRequestMock(this.searchMock);
+        lib.mockRequestFromFile('/api/rules/search', 'search-custom-rules2.json');
+      })
+
+      .then(function () {
+        test.assertElementCount('#coding-rules-detail-custom-rules .coding-rules-detail-list-name', 1);
+        casper.click('.js-create-custom-rule');
+        casper.fillForm('.modal form', {
+          name: 'test',
+          markdown_description: 'test'
+        });
+        casper.click('#coding-rules-custom-rule-creation-create');
+        lib.waitForElementCount('#coding-rules-detail-custom-rules .coding-rules-detail-list-name', 2, function () {
+          test.assert(true); // put dummy assert into wait statement
+        });
+      })
+
+      .run(function () {
+        test.done();
+      });
+});
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-custom-rules/app.json b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-custom-rules/app.json
new file mode 100644 (file)
index 0000000..f66b07b
--- /dev/null
@@ -0,0 +1,168 @@
+{
+  "canWrite": true,
+  "qualityprofiles": [
+    {
+      "key": "java-default-with-mojo-conventions-49307",
+      "name": "Default - Maven Conventions",
+      "lang": "java",
+      "parentKey": "java-top-profile-without-formatting-conventions-50037"
+    },
+    {
+      "key": "java-default-with-sonarsource-conventions-27339",
+      "name": "Default - SonarSource conventions",
+      "lang": "java",
+      "parentKey": "java-top-profile-without-formatting-conventions-50037"
+    },
+    {
+      "key": "java-top-profile-without-formatting-conventions-50037",
+      "name": "Default - Top",
+      "lang": "java"
+    },
+    {
+      "key": "java-findbugs-14954",
+      "name": "FindBugs",
+      "lang": "java"
+    },
+    {
+      "key": "java-for-sq-java-plugin-only-92289",
+      "name": "For SQ Java Plugin Only",
+      "lang": "java",
+      "parentKey": "java-default-with-sonarsource-conventions-27339"
+    },
+    {
+      "key": "java-for-sq-only-95381",
+      "name": "For SQ Only",
+      "lang": "java",
+      "parentKey": "java-default-with-sonarsource-conventions-27339"
+    },
+    {
+      "key": "php-psr-2-06315",
+      "name": "PSR-2",
+      "lang": "php"
+    },
+    {
+      "key": "java-sonar-way-80423",
+      "name": "Sonar way",
+      "lang": "java"
+    },
+    {
+      "key": "js-sonar-way",
+      "name": "Sonar way",
+      "lang": "js"
+    },
+    {
+      "key": "php-sonar-way-05548",
+      "name": "Sonar way",
+      "lang": "php"
+    },
+    {
+      "key": "py-sonar-way-80265",
+      "name": "Sonar way",
+      "lang": "py"
+    },
+    {
+      "key": "java-without-findbugs",
+      "name": "Without Findbugs",
+      "lang": "java"
+    }
+  ],
+  "languages": {
+    "py": "Python",
+    "js": "JavaScript",
+    "php": "PHP",
+    "java": "Java"
+  },
+  "repositories": [
+    {
+      "key": "common-java",
+      "name": "Common SonarQube",
+      "language": "java"
+    },
+    {
+      "key": "common-js",
+      "name": "Common SonarQube",
+      "language": "js"
+    },
+    {
+      "key": "common-php",
+      "name": "Common SonarQube",
+      "language": "php"
+    },
+    {
+      "key": "common-py",
+      "name": "Common SonarQube",
+      "language": "py"
+    },
+    {
+      "key": "Pylint",
+      "name": "Pylint",
+      "language": "py"
+    },
+    {
+      "key": "javascript",
+      "name": "SonarQube",
+      "language": "js"
+    },
+    {
+      "key": "php",
+      "name": "SonarQube",
+      "language": "php"
+    },
+    {
+      "key": "python",
+      "name": "SonarQube",
+      "language": "py"
+    },
+    {
+      "key": "squid",
+      "name": "SonarQube",
+      "language": "java"
+    }
+  ],
+  "statuses": {
+    "BETA": "Beta",
+    "DEPRECATED": "Deprecated",
+    "READY": "Ready"
+  },
+  "characteristics": {
+    "UNDERSTANDABILITY": "Maintainability: Understandability",
+    "MAINTAINABILITY": "Maintainability",
+    "TIME_ZONE_RELATED_PORTABILITY": "Portability: Time zone related portability",
+    "READABILITY": "Maintainability: Readability",
+    "SECURITY_FEATURES": "Security: Security features",
+    "ARCHITECTURE_RELIABILITY": "Reliability: Architecture related reliability",
+    "OS_RELATED_PORTABILITY": "Portability: OS related portability",
+    "EXCEPTION_HANDLING": "Reliability: Exception handling",
+    "LOGIC_CHANGEABILITY": "Changeability: Logic related changeability",
+    "SOFTWARE_RELATED_PORTABILITY": "Portability: Software related portability",
+    "INPUT_VALIDATION_AND_REPRESENTATION": "Security: Input validation and representation",
+    "LANGUAGE_RELATED_PORTABILITY": "Portability: Language related portability",
+    "ERRORS": "Security: Errors",
+    "SECURITY": "Security",
+    "RELIABILITY": "Reliability",
+    "PORTABILITY": "Portability",
+    "HARDWARE_RELATED_PORTABILITY": "Portability: Hardware related portability",
+    "SYNCHRONIZATION_RELIABILITY": "Reliability: Synchronization related reliability",
+    "TRANSPORTABILITY": "Reusability: Transportability",
+    "COMPILER_RELATED_PORTABILITY": "Portability: Compiler related portability",
+    "RESOURCE_RELIABILITY": "Reliability: Resource",
+    "CPU_EFFICIENCY": "Efficiency: Processor use",
+    "EFFICIENCY": "Efficiency",
+    "CHANGEABILITY": "Changeability",
+    "DATA_CHANGEABILITY": "Changeability: Data related changeability",
+    "API_ABUSE": "Security: API abuse",
+    "ARCHITECTURE_CHANGEABILITY": "Changeability: Architecture related changeability",
+    "UNIT_TESTS": "Reliability: Unit tests",
+    "INSTRUCTION_RELIABILITY": "Reliability: Instruction related reliability",
+    "REUSABILITY": "Reusability",
+    "MODULARITY": "Reusability: Modularity",
+    "UNIT_TESTABILITY": "Testability: Unit level testability",
+    "TESTABILITY": "Testability",
+    "INTEGRATION_TESTABILITY": "Testability: Integration level testability",
+    "NETWORK_USE": "Efficiency: Network use",
+    "MEMORY_EFFICIENCY": "Efficiency: Memory use",
+    "DATA_RELIABILITY": "Reliability: Data related reliability",
+    "FAULT_TOLERANCE": "Reliability: Fault tolerance",
+    "LOGIC_RELIABILITY": "Reliability: Logic related reliability"
+  }
+}
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-custom-rules/search-custom-rules.json b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-custom-rules/search-custom-rules.json
new file mode 100644 (file)
index 0000000..db83a85
--- /dev/null
@@ -0,0 +1,26 @@
+{
+  "total": 1,
+  "p": 1,
+  "ps": 10,
+  "rules": [
+    {
+      "key": "squid:Do_not_use_org_h2_util_StringUtils",
+      "name": "Do not use org.h2.util.StringUtils",
+      "severity": "MAJOR",
+      "params": [
+        {
+          "key": "fromClasses",
+          "htmlDesc": "Optional. If this property is not defined, all classes should adhere to this constraint. Ex : *<em>.web.</em>*",
+          "type": "STRING",
+          "defaultValue": ""
+        },
+        {
+          "key": "toClasses",
+          "htmlDesc": "Mandatory. Ex : java.util.Vector, java.util.Hashtable, java.util.Enumeration",
+          "type": "STRING",
+          "defaultValue": "org.h2.util.StringUtils"
+        }
+      ]
+    }
+  ]
+}
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-custom-rules/search-custom-rules2.json b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-custom-rules/search-custom-rules2.json
new file mode 100644 (file)
index 0000000..0e4184e
--- /dev/null
@@ -0,0 +1,45 @@
+{
+  "total": 2,
+  "p": 1,
+  "ps": 10,
+  "rules": [
+    {
+      "key": "squid:Do_not_use_org_h2_util_StringUtils",
+      "name": "Do not use org.h2.util.StringUtils",
+      "severity": "MAJOR",
+      "params": [
+        {
+          "key": "fromClasses",
+          "htmlDesc": "Optional. If this property is not defined, all classes should adhere to this constraint. Ex : *<em>.web.</em>*",
+          "type": "STRING",
+          "defaultValue": ""
+        },
+        {
+          "key": "toClasses",
+          "htmlDesc": "Mandatory. Ex : java.util.Vector, java.util.Hashtable, java.util.Enumeration",
+          "type": "STRING",
+          "defaultValue": "org.h2.util.StringUtils"
+        }
+      ]
+    },
+    {
+      "key": "squid:Do_not_use_edu_emory_mathcs_backport_java_util_Collections",
+      "name": "Do not use edu.emory.mathcs.backport.java.util.Collections",
+      "severity": "MAJOR",
+      "params": [
+        {
+          "key": "fromClasses",
+          "htmlDesc": "Optional. If this property is not defined, all classes should adhere to this constraint. Ex : *<em>.web.</em>*",
+          "type": "STRING",
+          "defaultValue": ""
+        },
+        {
+          "key": "toClasses",
+          "htmlDesc": "Mandatory. Ex : java.util.Vector, java.util.Hashtable, java.util.Enumeration",
+          "type": "STRING",
+          "defaultValue": "edu.emory.mathcs.backport.java.util.Collections"
+        }
+      ]
+    }
+  ]
+}
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-custom-rules/search.json b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-custom-rules/search.json
new file mode 100644 (file)
index 0000000..40ab6ae
--- /dev/null
@@ -0,0 +1,36 @@
+{
+  "total": 1,
+  "p": 1,
+  "ps": 200,
+  "rules": [
+    {
+      "key": "squid:ArchitecturalConstraint",
+      "name": "Architectural constraint",
+      "lang": "java",
+      "langName": "Java",
+      "sysTags": [],
+      "tags": [],
+      "status": "READY"
+    }
+  ],
+  "facets": [
+    {
+      "property": "languages",
+      "values": [
+        {
+          "val": "java",
+          "count": 1
+        }
+      ]
+    },
+    {
+      "property": "repositories",
+      "values": [
+        {
+          "val": "squid",
+          "count": 1
+        }
+      ]
+    }
+  ]
+}
diff --git a/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-custom-rules/show.json b/server/sonar-web/src/main/js/tests/e2e/tests/coding-rules-page-should-create-custom-rules/show.json
new file mode 100644 (file)
index 0000000..517c5f6
--- /dev/null
@@ -0,0 +1,37 @@
+{
+  "rule": {
+    "key": "squid:ArchitecturalConstraint",
+    "repo": "squid",
+    "name": "Architectural constraint",
+    "createdAt": "2013-03-27T09:52:40+0100",
+    "severity": "MAJOR",
+    "status": "READY",
+    "internalKey": "ArchitecturalConstraint",
+    "isTemplate": true,
+    "tags": [],
+    "sysTags": [],
+    "lang": "java",
+    "langName": "Java",
+    "htmlDesc": "<p>A source code comply to an architectural model when it fully\n\tadheres to a set of architectural constraints. A constraint allows to\n\tdeny references between classes by pattern.</p>\n<p>You can for instance use this rule to :</p>\n<ul>\n\t<li>forbid access to **.web.** from **.dao.** classes</li>\n\t<li>forbid access to java.util.Vector, java.util.Hashtable and\n\t\tjava.util.Enumeration from any classes</li>\n\t<li>forbid access to java.sql.** from **.ui.** and **.web.**\n\t\tclasses</li>\n</ul>",
+    "debtChar": "CHANGEABILITY",
+    "debtSubChar": "ARCHITECTURE_CHANGEABILITY",
+    "debtCharName": "Changeability",
+    "debtSubCharName": "Architecture related changeability",
+    "debtOverloaded": true,
+    "debtRemFnType": "LINEAR",
+    "debtRemFnCoeff": "3h",
+    "params": [
+      {
+        "key": "fromClasses",
+        "htmlDesc": "Optional. If this property is not defined, all classes should adhere to this constraint. Ex : *<em>.web.</em>*",
+        "type": "STRING"
+      },
+      {
+        "key": "toClasses",
+        "htmlDesc": "Mandatory. Ex : java.util.Vector, java.util.Hashtable, java.util.Enumeration",
+        "type": "STRING"
+      }
+    ]
+  },
+  "actives": []
+}