]> source.dussan.org Git - sonarqube.git/commitdiff
use nvm to install node.js 4, add mocha tests
authorStas Vilchik <vilchiks@gmail.com>
Fri, 25 Sep 2015 15:08:50 +0000 (17:08 +0200)
committerStas Vilchik <vilchiks@gmail.com>
Wed, 30 Sep 2015 08:18:55 +0000 (10:18 +0200)
17 files changed:
server/sonar-web/.eslintignore
server/sonar-web/package.json
server/sonar-web/src/main/js/apps/nav/component/component-nav-breadcrumbs.jsx
server/sonar-web/src/main/js/components/issue/models/issue.js
server/sonar-web/test/intern.js
server/sonar-web/test/unit/application.spec.js [deleted file]
server/sonar-web/test/unit/code-with-issue-locations-helper.spec.js [deleted file]
server/sonar-web/test/unit/issue.spec.js [deleted file]
server/sonar-web/test/unit/nav/component/component-nav-breadcrumbs.spec.js [deleted file]
server/sonar-web/tests/application-test.js [new file with mode: 0644]
server/sonar-web/tests/apps/background-tasks-test.js [new file with mode: 0644]
server/sonar-web/tests/apps/nav-test.js [new file with mode: 0644]
server/sonar-web/tests/components/issue-test.js [new file with mode: 0644]
server/sonar-web/tests/components/source-viewer-test.js [new file with mode: 0644]
server/sonar-web/tests/jsdom-setup.js [new file with mode: 0644]
server/sonar-web/tests/mocha.opts [new file with mode: 0644]
travis.sh

index 681af342526ace04d6abe0bec956f3e7105b81f3..d2424ba88a4d151dca936b1d9ceebfdfef48794f 100644 (file)
@@ -1 +1,2 @@
 src/main/js/libs/third-party
+tests
index 7255e643d9ae25a052498c3030b784ce28631be4..0aede03a2f975a24b788d51833d95b44e4b7bab8 100644 (file)
@@ -5,6 +5,9 @@
   "repository": "SonarSource/sonarqube",
   "license": "LGPL-3.0",
   "devDependencies": {
+    "babel": "^5.8.23",
+    "backbone": "^1.1.2",
+    "chai": "^3.3.0",
     "grunt": "0.4.5",
     "grunt-babel": "5.0.1",
     "grunt-cli": "0.1.13",
     "grunt-text-replace": "0.4.0",
     "intern": "3.0.0",
     "jit-grunt": "0.9.1",
+    "jquery": "2.1.4",
+    "jsdom": "^6.5.1",
+    "mocha": "^2.3.3",
+    "react": "0.13.3",
     "sinon": "^1.15.4",
-    "time-grunt": "1.2.1"
+    "sinon-chai": "^2.8.0",
+    "time-grunt": "1.2.1",
+    "underscore": "1.8.3"
   },
   "scripts": {
     "build-fast": "./node_modules/.bin/grunt build-fast",
     "build": "./node_modules/.bin/grunt build",
-    "build-test": "./node_modules/.bin/grunt build-test",
-    "build-coverage": "./node_modules/.bin/grunt build-coverage",
-    "test": "./node_modules/.bin/grunt test",
-    "coverage": "./node_modules/.bin/grunt coverage"
+    "test": "./node_modules/.bin/mocha --opts tests/mocha.opts tests"
   }
 }
index 1bb643c11ac0c6709e1c27eec13ae8e04c019b71..d128d5ac002978325124a3b95f52e2c3ce72d6b4 100644 (file)
@@ -1,5 +1,5 @@
 import React from 'react';
-import QualifierIcon from 'components/shared/qualifier-icon';
+import QualifierIcon from '../../../components/shared/qualifier-icon';
 
 export default React.createClass({
   render() {
index df070f19a39c16005e828cfcbc45254ff1a23966..c619361c39901f9ddf8e132520b150613366d0f4 100644 (file)
@@ -11,11 +11,11 @@ export default Backbone.Model.extend({
   },
 
   url: function () {
-    return baseUrl + '/api/issues';
+    return window.baseUrl + '/api/issues';
   },
 
   urlRoot: function () {
-    return baseUrl + '/api/issues';
+    return window.baseUrl + '/api/issues';
   },
 
   parse: function (r) {
index 70273be7ead4873a7c1b72b8ded908a474c828ab..20e008b1e2a6c7ef4cb089099bc2fddee0a380fe 100644 (file)
@@ -14,14 +14,25 @@ define(['intern'], function (intern) {
       { id: 'LcovHtml', directory: 'target/web-tests' }
     ],
 
-    suites: [
-      'test/unit/application.spec',
-      'test/unit/issue.spec',
-      'test/unit/code-with-issue-locations-helper.spec',
-      'test/unit/nav/component/component-nav-breadcrumbs.spec'
-    ],
+    suites: [],
 
-    functionalSuites: [],
+    functionalSuites: [
+      'test/medium/api-documentation.spec',
+      'test/medium/coding-rules.spec',
+      'test/medium/computation.spec',
+      'test/medium/custom-measures.spec',
+      'test/medium/global-permissions.spec',
+      'test/medium/groups.spec',
+      'test/medium/issues.spec',
+      'test/medium/maintenance.spec',
+      'test/medium/metrics.spec',
+      'test/medium/project-permissions.spec',
+      'test/medium/quality-gates.spec',
+      'test/medium/quality-profiles.spec',
+      'test/medium/source-viewer.spec',
+      'test/medium/update-center.spec',
+      'test/medium/users.spec'
+    ],
 
     tunnel: tunnel,
     environments: [
diff --git a/server/sonar-web/test/unit/application.spec.js b/server/sonar-web/test/unit/application.spec.js
deleted file mode 100644 (file)
index b3f7e76..0000000
+++ /dev/null
@@ -1,247 +0,0 @@
-define(function (require) {
-  var bdd = require('intern!bdd');
-  var assert = require('intern/chai!assert');
-
-  require('intern/order!build/js/libs/translate.js');
-  require('intern/order!build/js/libs/third-party/jquery.js');
-  require('intern/order!build/js/libs/third-party/underscore.js');
-  require('intern/order!build/js/libs/third-party/keymaster.js');
-  require('intern/order!build/js/libs/third-party/numeral.js');
-  require('intern/order!build/js/libs/third-party/numeral-languages.js');
-  require('intern/order!build/js/libs/application.js');
-
-  bdd.describe('Application', function () {
-    bdd.describe('#collapsedDirFromPath()', function () {
-      bdd.it('should return null when pass null', function () {
-        assert.isNull(window.collapsedDirFromPath(null));
-      });
-
-      bdd.it('should return "/" when pass "/"', function () {
-        assert.equal(window.collapsedDirFromPath('/'), '/');
-      });
-
-      bdd.it('should not cut short path', function () {
-        assert.equal(window.collapsedDirFromPath('src/main/js/components/state.js'), 'src/main/js/components/');
-      });
-
-      bdd.it('should cut long path', function () {
-        assert.equal(window.collapsedDirFromPath('src/main/js/components/navigator/app/models/state.js'),
-            'src/.../js/components/navigator/app/models/');
-      });
-
-      bdd.it('should cut very long path', function () {
-        assert.equal(window.collapsedDirFromPath('src/main/another/js/components/navigator/app/models/state.js'),
-            'src/.../js/components/navigator/app/models/');
-      });
-    });
-
-    bdd.describe('#fileFromPath()', function () {
-      bdd.it('should return null when pass null', function () {
-        assert.isNull(window.fileFromPath(null));
-      });
-
-      bdd.it('should return empty string when pass "/"', function () {
-        assert.equal(window.fileFromPath('/'), '');
-      });
-
-      bdd.it('should return file name when pass only file name', function () {
-        assert.equal(window.fileFromPath('file.js'), 'file.js');
-      });
-
-      bdd.it('should return file name when pass file path', function () {
-        assert.equal(window.fileFromPath('src/main/js/file.js'), 'file.js');
-      });
-
-      bdd.it('should return file name when pass file name without extension', function () {
-        assert.equal(window.fileFromPath('src/main/file'), 'file');
-      });
-    });
-
-    bdd.describe('Measures', function () {
-      var HOURS_IN_DAY = 8,
-          ONE_MINUTE = 1,
-          ONE_HOUR = ONE_MINUTE * 60,
-          ONE_DAY = HOURS_IN_DAY * ONE_HOUR;
-
-      bdd.before(function () {
-        window.messages = {
-          'work_duration.x_days': '{0}d',
-          'work_duration.x_hours': '{0}h',
-          'work_duration.x_minutes': '{0}min',
-          'work_duration.about': '~ {0}'
-        };
-        window.SS = { hoursInDay: HOURS_IN_DAY };
-      });
-
-      bdd.describe('#formatMeasure()', function () {
-        bdd.it('should format INT', function () {
-          assert.equal(window.formatMeasure(0, 'INT'), '0');
-          assert.equal(window.formatMeasure(1, 'INT'), '1');
-          assert.equal(window.formatMeasure(-5, 'INT'), '-5');
-          assert.equal(window.formatMeasure(999, 'INT'), '999');
-          assert.equal(window.formatMeasure(1000, 'INT'), '1,000');
-          assert.equal(window.formatMeasure(1529, 'INT'), '1,529');
-          assert.equal(window.formatMeasure(10000, 'INT'), '10,000');
-          assert.equal(window.formatMeasure(1234567890, 'INT'), '1,234,567,890');
-        });
-
-        bdd.it('should format SHORT_INT', function () {
-          assert.equal(window.formatMeasure(0, 'SHORT_INT'), '0');
-          assert.equal(window.formatMeasure(1, 'SHORT_INT'), '1');
-          assert.equal(window.formatMeasure(999, 'SHORT_INT'), '999');
-          assert.equal(window.formatMeasure(1000, 'SHORT_INT'), '1k');
-          assert.equal(window.formatMeasure(1529, 'SHORT_INT'), '1.5k');
-          assert.equal(window.formatMeasure(10000, 'SHORT_INT'), '10k');
-          assert.equal(window.formatMeasure(10678, 'SHORT_INT'), '11k');
-          assert.equal(window.formatMeasure(1234567890, 'SHORT_INT'), '1b');
-        });
-
-        bdd.it('should format FLOAT', function () {
-          assert.equal(window.formatMeasure(0.0, 'FLOAT'), '0.0');
-          assert.equal(window.formatMeasure(1.0, 'FLOAT'), '1.0');
-          assert.equal(window.formatMeasure(1.3, 'FLOAT'), '1.3');
-          assert.equal(window.formatMeasure(1.34, 'FLOAT'), '1.3');
-          assert.equal(window.formatMeasure(50.89, 'FLOAT'), '50.9');
-          assert.equal(window.formatMeasure(100.0, 'FLOAT'), '100.0');
-          assert.equal(window.formatMeasure(123.456, 'FLOAT'), '123.5');
-          assert.equal(window.formatMeasure(123456.7, 'FLOAT'), '123,456.7');
-          assert.equal(window.formatMeasure(1234567890.0, 'FLOAT'), '1,234,567,890.0');
-        });
-
-        bdd.it('should format PERCENT', function () {
-          assert.equal(window.formatMeasure(0.0, 'PERCENT'), '0.0%');
-          assert.equal(window.formatMeasure(1.0, 'PERCENT'), '1.0%');
-          assert.equal(window.formatMeasure(1.3, 'PERCENT'), '1.3%');
-          assert.equal(window.formatMeasure(1.34, 'PERCENT'), '1.3%');
-          assert.equal(window.formatMeasure(50.89, 'PERCENT'), '50.9%');
-          assert.equal(window.formatMeasure(100.0, 'PERCENT'), '100.0%');
-        });
-
-        bdd.it('should format WORK_DUR', function () {
-          assert.equal(window.formatMeasure(0, 'WORK_DUR'), '0');
-          assert.equal(window.formatMeasure(5 * ONE_DAY, 'WORK_DUR'), '5d');
-          assert.equal(window.formatMeasure(2 * ONE_HOUR, 'WORK_DUR'), '2h');
-          assert.equal(window.formatMeasure(ONE_MINUTE, 'WORK_DUR'), '1min');
-          assert.equal(window.formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR, 'WORK_DUR'), '5d 2h');
-          assert.equal(window.formatMeasure(2 * ONE_HOUR + ONE_MINUTE, 'WORK_DUR'), '2h 1min');
-          assert.equal(window.formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'WORK_DUR'), '5d 2h');
-          assert.equal(window.formatMeasure(15 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'WORK_DUR'), '15d');
-          assert.equal(window.formatMeasure(-5 * ONE_DAY, 'WORK_DUR'), '-5d');
-          assert.equal(window.formatMeasure(-2 * ONE_HOUR, 'WORK_DUR'), '-2h');
-          assert.equal(window.formatMeasure(-1 * ONE_MINUTE, 'WORK_DUR'), '-1min');
-        });
-
-        bdd.it('should format SHORT_WORK_DUR', function () {
-          assert.equal(window.formatMeasure(0, 'SHORT_WORK_DUR'), '0');
-          assert.equal(window.formatMeasure(5 * ONE_DAY, 'SHORT_WORK_DUR'), '5d');
-          assert.equal(window.formatMeasure(2 * ONE_HOUR, 'SHORT_WORK_DUR'), '2h');
-          assert.equal(window.formatMeasure(ONE_MINUTE, 'SHORT_WORK_DUR'), '1min');
-          assert.equal(window.formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR, 'SHORT_WORK_DUR'), '~ 5d');
-          assert.equal(window.formatMeasure(2 * ONE_HOUR + ONE_MINUTE, 'SHORT_WORK_DUR'), '~ 2h');
-          assert.equal(window.formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'SHORT_WORK_DUR'), '~ 5d');
-          assert.equal(window.formatMeasure(15 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'SHORT_WORK_DUR'), '~ 15d');
-          assert.equal(window.formatMeasure(7 * ONE_MINUTE, 'SHORT_WORK_DUR'), '7min');
-          assert.equal(window.formatMeasure(-5 * ONE_DAY, 'SHORT_WORK_DUR'), '-5d');
-          assert.equal(window.formatMeasure(-2 * ONE_HOUR, 'SHORT_WORK_DUR'), '-2h');
-          assert.equal(window.formatMeasure(-1 * ONE_MINUTE, 'SHORT_WORK_DUR'), '-1min');
-
-          assert.equal(window.formatMeasure(1529 * ONE_DAY, 'SHORT_WORK_DUR'), '1.5kd');
-          assert.equal(window.formatMeasure(1234567 * ONE_DAY, 'SHORT_WORK_DUR'), '1md');
-          assert.equal(window.formatMeasure(1234567 * ONE_DAY + 2 * ONE_HOUR, 'SHORT_WORK_DUR'), '1md');
-        });
-
-        bdd.it('should format RATING', function () {
-          assert.equal(window.formatMeasure(1, 'RATING'), 'A');
-          assert.equal(window.formatMeasure(2, 'RATING'), 'B');
-          assert.equal(window.formatMeasure(3, 'RATING'), 'C');
-          assert.equal(window.formatMeasure(4, 'RATING'), 'D');
-          assert.equal(window.formatMeasure(5, 'RATING'), 'E');
-        });
-
-        bdd.it('should not format unknown type', function () {
-          assert.equal(window.formatMeasure('random value', 'RANDOM_TYPE'), 'random value');
-        });
-
-        bdd.it('should not fail without parameters', function () {
-          assert.isNull(window.formatMeasure());
-        });
-      });
-
-      bdd.describe('#formatMeasureVariation()', function () {
-        bdd.it('should format INT', function () {
-          assert.equal(window.formatMeasureVariation(0, 'INT'), '0');
-          assert.equal(window.formatMeasureVariation(1, 'INT'), '+1');
-          assert.equal(window.formatMeasureVariation(-1, 'INT'), '-1');
-          assert.equal(window.formatMeasureVariation(1529, 'INT'), '+1,529');
-          assert.equal(window.formatMeasureVariation(-1529, 'INT'), '-1,529');
-        });
-
-        bdd.it('should format SHORT_INT', function () {
-          assert.equal(window.formatMeasureVariation(0, 'SHORT_INT'), '0');
-          assert.equal(window.formatMeasureVariation(1, 'SHORT_INT'), '+1');
-          assert.equal(window.formatMeasureVariation(-1, 'SHORT_INT'), '-1');
-          assert.equal(window.formatMeasureVariation(1529, 'SHORT_INT'), '+1.5k');
-          assert.equal(window.formatMeasureVariation(-1529, 'SHORT_INT'), '-1.5k');
-          assert.equal(window.formatMeasureVariation(10678, 'SHORT_INT'), '+11k');
-          assert.equal(window.formatMeasureVariation(-10678, 'SHORT_INT'), '-11k');
-        });
-
-        bdd.it('should format FLOAT', function () {
-          assert.equal(window.formatMeasureVariation(0.0, 'FLOAT'), '0');
-          assert.equal(window.formatMeasureVariation(1.0, 'FLOAT'), '+1.0');
-          assert.equal(window.formatMeasureVariation(-1.0, 'FLOAT'), '-1.0');
-          assert.equal(window.formatMeasureVariation(50.89, 'FLOAT'), '+50.9');
-          assert.equal(window.formatMeasureVariation(-50.89, 'FLOAT'), '-50.9');
-        });
-
-        bdd.it('should format PERCENT', function () {
-          assert.equal(window.formatMeasureVariation(0.0, 'PERCENT'), '0%');
-          assert.equal(window.formatMeasureVariation(1.0, 'PERCENT'), '+1.0%');
-          assert.equal(window.formatMeasureVariation(-1.0, 'PERCENT'), '-1.0%');
-          assert.equal(window.formatMeasureVariation(50.89, 'PERCENT'), '+50.9%');
-          assert.equal(window.formatMeasureVariation(-50.89, 'PERCENT'), '-50.9%');
-        });
-
-        bdd.it('should format WORK_DUR', function () {
-          assert.equal(window.formatMeasureVariation(0, 'WORK_DUR'), '0');
-          assert.equal(window.formatMeasureVariation(5 * ONE_DAY, 'WORK_DUR'), '+5d');
-          assert.equal(window.formatMeasureVariation(2 * ONE_HOUR, 'WORK_DUR'), '+2h');
-          assert.equal(window.formatMeasureVariation(ONE_MINUTE, 'WORK_DUR'), '+1min');
-          assert.equal(window.formatMeasureVariation(-5 * ONE_DAY, 'WORK_DUR'), '-5d');
-          assert.equal(window.formatMeasureVariation(-2 * ONE_HOUR, 'WORK_DUR'), '-2h');
-          assert.equal(window.formatMeasureVariation(-1 * ONE_MINUTE, 'WORK_DUR'), '-1min');
-        });
-
-        bdd.it('should not format unknown type', function () {
-          assert.equal(window.formatMeasureVariation('random value', 'RANDOM_TYPE'), 'random value');
-        });
-
-        bdd.it('should not fail without parameters', function () {
-          assert.isNull(window.formatMeasureVariation());
-        });
-      });
-    });
-
-    bdd.describe('Severity Comparators', function () {
-      bdd.describe('#severityComparator', function () {
-        bdd.it('should have correct order', function () {
-          assert.equal(window.severityComparator('BLOCKER'), 0);
-          assert.equal(window.severityComparator('CRITICAL'), 1);
-          assert.equal(window.severityComparator('MAJOR'), 2);
-          assert.equal(window.severityComparator('MINOR'), 3);
-          assert.equal(window.severityComparator('INFO'), 4);
-        });
-      });
-
-      bdd.describe('#severityColumnsComparator', function () {
-        bdd.it('should have correct order', function () {
-          assert.equal(window.severityColumnsComparator('BLOCKER'), 0);
-          assert.equal(window.severityColumnsComparator('CRITICAL'), 2);
-          assert.equal(window.severityColumnsComparator('MAJOR'), 4);
-          assert.equal(window.severityColumnsComparator('MINOR'), 1);
-          assert.equal(window.severityColumnsComparator('INFO'), 3);
-        });
-      });
-    });
-  });
-});
diff --git a/server/sonar-web/test/unit/code-with-issue-locations-helper.spec.js b/server/sonar-web/test/unit/code-with-issue-locations-helper.spec.js
deleted file mode 100644 (file)
index 7c3938c..0000000
+++ /dev/null
@@ -1,62 +0,0 @@
-define(function (require) {
-  var bdd = require('intern!bdd');
-  var assert = require('intern/chai!assert');
-
-  var helper = require('build/js/components/source-viewer/helpers/code-with-issue-locations-helper');
-
-  bdd.describe('Code With Issue Locations Helper', function () {
-    bdd.it('should exist', function () {
-      assert.equal(typeof helper, 'function');
-    });
-
-    bdd.it('should mark one location', function () {
-      var code = '<span class="k">if</span> (<span class="sym-2 sym">a</span> + <span class="c">1</span>) {',
-          locations = [{ from: 1, to: 5 }],
-          result = helper(code, locations, 'x');
-      assert.equal(result,
-          '<span class="k">i</span><span class="k x">f</span><span class=" x"> (</span><span class="sym-2 sym x">a</span><span class=""> + </span><span class="c">1</span><span class="">) {</span>');
-    });
-
-    bdd.it('should mark two locations', function () {
-      var code = 'abcdefghijklmnopqrst',
-          locations = [
-            { from: 1, to: 6 },
-            { from: 11, to: 16 }
-          ],
-          result = helper(code, locations, 'x');
-      assert.equal(result,
-          ['<span class="">a</span>',
-           '<span class=" x">bcdef</span>',
-           '<span class="">ghijk</span>',
-           '<span class=" x">lmnop</span>',
-           '<span class="">qrst</span>'].join(''));
-    });
-
-    bdd.it('should mark one locations', function () {
-      var code = '<span class="cppd"> * Copyright (C) 2008-2014 SonarSource</span>',
-          locations = [{ from: 15, to: 20 }],
-          result = helper(code, locations, 'x');
-      assert.equal(result,
-          '<span class="cppd"> * Copyright (C</span><span class="cppd x">) 200</span><span class="cppd">8-2014 SonarSource</span>');
-    });
-
-    bdd.it('should mark two locations', function () {
-      var code = '<span class="cppd"> * Copyright (C) 2008-2014 SonarSource</span>',
-          locations = [
-            { from: 24, to: 29 },
-            { from: 15, to: 20 }
-          ],
-          result = helper(code, locations, 'x');
-      assert.equal(result,
-          '<span class="cppd"> * Copyright (C</span><span class="cppd x">) 200</span><span class="cppd">8-20</span><span class="cppd x">14 So</span><span class="cppd">narSource</span>');
-      //   <span class="cppd"> * Copyright (C</span><span class="cppd x">) 200</span><span class="cppd">8-20</span><span class="cppd x">4 So</span><span class="cppd">narSource</span>
-    });
-
-    bdd.it('should parse line with < and >', function () {
-      var code = '<span class="j">#include &lt;stdio.h&gt;</span>',
-          result = helper(code, []);
-      assert.equal(result, '<span class="j">#include &lt;stdio.h&gt;</span>');
-    });
-  });
-});
-
diff --git a/server/sonar-web/test/unit/issue.spec.js b/server/sonar-web/test/unit/issue.spec.js
deleted file mode 100644 (file)
index d8ce9ed..0000000
+++ /dev/null
@@ -1,179 +0,0 @@
-define(function (require) {
-  var bdd = require('intern!bdd');
-  var assert = require('intern/chai!assert');
-
-  require('intern/order!build/js/libs/translate.js');
-  require('intern/order!build/js/libs/third-party/jquery.js');
-  require('intern/order!build/js/libs/third-party/underscore.js');
-  require('intern/order!build/js/libs/third-party/backbone.js');
-  require('intern/order!build/js/libs/third-party/keymaster.js');
-  require('intern/order!build/js/libs/third-party/numeral.js');
-  require('intern/order!build/js/libs/third-party/numeral-languages.js');
-  require('intern/order!build/js/libs/application.js');
-  require('intern/order!node_modules/sinon/pkg/sinon');
-
-  var Issue = require('build/js/components/issue/models/issue');
-
-  bdd.describe('Issue', function () {
-    bdd.before(function () {
-      window.baseUrl = '';
-    });
-
-    bdd.it('should have correct urlRoot', function () {
-      var issue = new Issue();
-      assert.equal(issue.urlRoot(), '/api/issues');
-    });
-
-    bdd.it('should parse response without root issue object', function () {
-      var issue = new Issue();
-      var example = { a: 1 };
-      assert.deepEqual(issue.parse(example), example);
-    });
-
-    bdd.it('should parse response with the root issue object', function () {
-      var issue = new Issue();
-      var example = { a: 1 };
-      assert.deepEqual(issue.parse({ issue: example }), example);
-    });
-
-    bdd.it('should reset attributes (no attributes initially)', function () {
-      var issue = new Issue();
-      var example = { a: 1 };
-      issue.reset(example);
-      assert.deepEqual(issue.toJSON(), example);
-    });
-
-    bdd.it('should reset attributes (override attribute)', function () {
-      var issue = new Issue({ a: 2 });
-      var example = { a: 1 };
-      issue.reset(example);
-      assert.deepEqual(issue.toJSON(), example);
-    });
-
-    bdd.it('should reset attributes (different attributes)', function () {
-      var issue = new Issue({ a: 2 });
-      var example = { b: 1 };
-      issue.reset(example);
-      assert.deepEqual(issue.toJSON(), example);
-    });
-
-    bdd.it('should unset `textRange` of a closed issue', function () {
-      var issue = new Issue();
-      var result = issue.parse({ issue: { status: 'CLOSED', textRange: { startLine: 5 } } });
-      assert.notOk(result.textRange);
-    });
-
-    bdd.it('should unset `flows` of a closed issue', function () {
-      var issue = new Issue();
-      var result = issue.parse({ issue: { status: 'CLOSED', flows: [1, 2, 3] } });
-      assert.deepEqual(result.flows, []);
-    });
-
-    bdd.describe('Actions', function () {
-      var stub;
-
-      bdd.beforeEach(function () {
-        stub = sinon.stub(jQuery, 'ajax');
-      });
-
-      bdd.afterEach(function () {
-        jQuery.ajax.restore();
-      });
-
-      bdd.it('should assign', function () {
-        new Issue({ key: 'issue-key' }).assign('admin');
-        assert.isTrue(stub.calledOnce);
-        assert.equal(stub.firstCall.args[0].url, '/api/issues/assign');
-        assert.deepEqual(stub.firstCall.args[0].data, { issue: 'issue-key', assignee: 'admin' });
-      });
-
-      bdd.it('should unassign', function () {
-        new Issue({ key: 'issue-key' }).assign();
-        assert.isTrue(stub.calledOnce);
-        assert.equal(stub.firstCall.args[0].url, '/api/issues/assign');
-        assert.deepEqual(stub.firstCall.args[0].data, { issue: 'issue-key', assignee: undefined });
-      });
-
-      bdd.it('should plan', function () {
-        new Issue({ key: 'issue-key' }).plan('plan');
-        assert.isTrue(stub.calledOnce);
-        assert.equal(stub.firstCall.args[0].url, '/api/issues/plan');
-        assert.deepEqual(stub.firstCall.args[0].data, { issue: 'issue-key', plan: 'plan' });
-      });
-
-      bdd.it('should unplan', function () {
-        new Issue({ key: 'issue-key' }).plan();
-        assert.isTrue(stub.calledOnce);
-        assert.equal(stub.firstCall.args[0].url, '/api/issues/plan');
-        assert.deepEqual(stub.firstCall.args[0].data, { issue: 'issue-key', plan: undefined });
-      });
-
-      bdd.it('should set severity', function () {
-        new Issue({ key: 'issue-key' }).setSeverity('BLOCKER');
-        assert.isTrue(stub.calledOnce);
-        assert.equal(stub.firstCall.args[0].url, '/api/issues/set_severity');
-        assert.deepEqual(stub.firstCall.args[0].data, { issue: 'issue-key', severity: 'BLOCKER' });
-      });
-    });
-
-    bdd.describe('#getLinearLocations', function () {
-      bdd.it('should return single line location', function () {
-        var issue = new Issue({ textRange: { startLine: 1, endLine: 1, startOffset: 0, endOffset: 10 } }),
-            locations = issue.getLinearLocations();
-        assert.equal(locations.length, 1);
-
-        assert.equal(locations[0].line, 1);
-        assert.equal(locations[0].from, 0);
-        assert.equal(locations[0].to, 10);
-      });
-
-      bdd.it('should return location not from 0', function () {
-        var issue = new Issue({ textRange: { startLine: 1, endLine: 1, startOffset: 5, endOffset: 10 } }),
-            locations = issue.getLinearLocations();
-        assert.equal(locations.length, 1);
-
-        assert.equal(locations[0].line, 1);
-        assert.equal(locations[0].from, 5);
-        assert.equal(locations[0].to, 10);
-      });
-
-      bdd.it('should return 2-lines location', function () {
-        var issue = new Issue({ textRange: { startLine: 2, endLine: 3, startOffset: 5, endOffset: 10 } }),
-            locations = issue.getLinearLocations();
-        assert.equal(locations.length, 2);
-
-        assert.equal(locations[0].line, 2);
-        assert.equal(locations[0].from, 5);
-        assert.equal(locations[0].to, 999999);
-
-        assert.equal(locations[1].line, 3);
-        assert.equal(locations[1].from, 0);
-        assert.equal(locations[1].to, 10);
-      });
-
-      bdd.it('should return 3-lines location', function () {
-        var issue = new Issue({ textRange: { startLine: 4, endLine: 6, startOffset: 5, endOffset: 10 } }),
-            locations = issue.getLinearLocations();
-        assert.equal(locations.length, 3);
-
-        assert.equal(locations[0].line, 4);
-        assert.equal(locations[0].from, 5);
-        assert.equal(locations[0].to, 999999);
-
-        assert.equal(locations[1].line, 5);
-        assert.equal(locations[1].from, 0);
-        assert.equal(locations[1].to, 999999);
-
-        assert.equal(locations[2].line, 6);
-        assert.equal(locations[2].from, 0);
-        assert.equal(locations[2].to, 10);
-      });
-
-      bdd.it('should return [] when no location', function () {
-        var issue = new Issue(),
-            locations = issue.getLinearLocations();
-        assert.equal(locations.length, 0);
-      });
-    });
-  });
-});
diff --git a/server/sonar-web/test/unit/nav/component/component-nav-breadcrumbs.spec.js b/server/sonar-web/test/unit/nav/component/component-nav-breadcrumbs.spec.js
deleted file mode 100644 (file)
index 4b2a5fe..0000000
+++ /dev/null
@@ -1,27 +0,0 @@
-define(function (require) {
-  var bdd = require('intern!bdd');
-  var assert = require('intern/chai!assert');
-
-  var React = require('react');
-  var TestUtils = React.addons.TestUtils;
-
-  var ComponentNavBreadcrumbs = require('build/js/apps/nav/component/component-nav-breadcrumbs');
-
-  bdd.describe('ComponentNavBreadcrumbs', function () {
-    bdd.it('should not render unless `props.breadcrumbs`', function () {
-      var result = React.renderToStaticMarkup(React.createElement(ComponentNavBreadcrumbs, null));
-      assert.equal(result, '<noscript></noscript>');
-    });
-
-    bdd.it('should not render breadcrumbs with one element', function () {
-      var breadcrumbs = [
-        { key: 'my-project', name: 'My Project', qualifier: 'TRK' }
-      ];
-      var result = TestUtils.renderIntoDocument(
-          React.createElement(ComponentNavBreadcrumbs, { breadcrumbs: breadcrumbs })
-      );
-      assert.equal(TestUtils.scryRenderedDOMComponentsWithTag(result, 'li').length, 1);
-      assert.equal(TestUtils.scryRenderedDOMComponentsWithTag(result, 'a').length, 1);
-    });
-  });
-});
diff --git a/server/sonar-web/tests/application-test.js b/server/sonar-web/tests/application-test.js
new file mode 100644 (file)
index 0000000..e7705ee
--- /dev/null
@@ -0,0 +1,234 @@
+describe.skip('Application', function () {
+  describe('#collapsedDirFromPath()', function () {
+    it('should return null when pass null', function () {
+      assert.isNull(window.collapsedDirFromPath(null));
+    });
+
+    it('should return "/" when pass "/"', function () {
+      assert.equal(window.collapsedDirFromPath('/'), '/');
+    });
+
+    it('should not cut short path', function () {
+      assert.equal(window.collapsedDirFromPath('src/main/js/components/state.js'), 'src/main/js/components/');
+    });
+
+    it('should cut long path', function () {
+      assert.equal(window.collapsedDirFromPath('src/main/js/components/navigator/app/models/state.js'),
+          'src/.../js/components/navigator/app/models/');
+    });
+
+    it('should cut very long path', function () {
+      assert.equal(window.collapsedDirFromPath('src/main/another/js/components/navigator/app/models/state.js'),
+          'src/.../js/components/navigator/app/models/');
+    });
+  });
+
+  describe('#fileFromPath()', function () {
+    it('should return null when pass null', function () {
+      assert.isNull(window.fileFromPath(null));
+    });
+
+    it('should return empty string when pass "/"', function () {
+      assert.equal(window.fileFromPath('/'), '');
+    });
+
+    it('should return file name when pass only file name', function () {
+      assert.equal(window.fileFromPath('file.js'), 'file.js');
+    });
+
+    it('should return file name when pass file path', function () {
+      assert.equal(window.fileFromPath('src/main/js/file.js'), 'file.js');
+    });
+
+    it('should return file name when pass file name without extension', function () {
+      assert.equal(window.fileFromPath('src/main/file'), 'file');
+    });
+  });
+
+  describe('Measures', function () {
+    var HOURS_IN_DAY = 8,
+        ONE_MINUTE = 1,
+        ONE_HOUR = ONE_MINUTE * 60,
+        ONE_DAY = HOURS_IN_DAY * ONE_HOUR;
+
+    before(function () {
+      window.messages = {
+        'work_duration.x_days': '{0}d',
+        'work_duration.x_hours': '{0}h',
+        'work_duration.x_minutes': '{0}min',
+        'work_duration.about': '~ {0}'
+      };
+      window.SS = { hoursInDay: HOURS_IN_DAY };
+    });
+
+    describe('#formatMeasure()', function () {
+      it('should format INT', function () {
+        assert.equal(window.formatMeasure(0, 'INT'), '0');
+        assert.equal(window.formatMeasure(1, 'INT'), '1');
+        assert.equal(window.formatMeasure(-5, 'INT'), '-5');
+        assert.equal(window.formatMeasure(999, 'INT'), '999');
+        assert.equal(window.formatMeasure(1000, 'INT'), '1,000');
+        assert.equal(window.formatMeasure(1529, 'INT'), '1,529');
+        assert.equal(window.formatMeasure(10000, 'INT'), '10,000');
+        assert.equal(window.formatMeasure(1234567890, 'INT'), '1,234,567,890');
+      });
+
+      it('should format SHORT_INT', function () {
+        assert.equal(window.formatMeasure(0, 'SHORT_INT'), '0');
+        assert.equal(window.formatMeasure(1, 'SHORT_INT'), '1');
+        assert.equal(window.formatMeasure(999, 'SHORT_INT'), '999');
+        assert.equal(window.formatMeasure(1000, 'SHORT_INT'), '1k');
+        assert.equal(window.formatMeasure(1529, 'SHORT_INT'), '1.5k');
+        assert.equal(window.formatMeasure(10000, 'SHORT_INT'), '10k');
+        assert.equal(window.formatMeasure(10678, 'SHORT_INT'), '11k');
+        assert.equal(window.formatMeasure(1234567890, 'SHORT_INT'), '1b');
+      });
+
+      it('should format FLOAT', function () {
+        assert.equal(window.formatMeasure(0.0, 'FLOAT'), '0.0');
+        assert.equal(window.formatMeasure(1.0, 'FLOAT'), '1.0');
+        assert.equal(window.formatMeasure(1.3, 'FLOAT'), '1.3');
+        assert.equal(window.formatMeasure(1.34, 'FLOAT'), '1.3');
+        assert.equal(window.formatMeasure(50.89, 'FLOAT'), '50.9');
+        assert.equal(window.formatMeasure(100.0, 'FLOAT'), '100.0');
+        assert.equal(window.formatMeasure(123.456, 'FLOAT'), '123.5');
+        assert.equal(window.formatMeasure(123456.7, 'FLOAT'), '123,456.7');
+        assert.equal(window.formatMeasure(1234567890.0, 'FLOAT'), '1,234,567,890.0');
+      });
+
+      it('should format PERCENT', function () {
+        assert.equal(window.formatMeasure(0.0, 'PERCENT'), '0.0%');
+        assert.equal(window.formatMeasure(1.0, 'PERCENT'), '1.0%');
+        assert.equal(window.formatMeasure(1.3, 'PERCENT'), '1.3%');
+        assert.equal(window.formatMeasure(1.34, 'PERCENT'), '1.3%');
+        assert.equal(window.formatMeasure(50.89, 'PERCENT'), '50.9%');
+        assert.equal(window.formatMeasure(100.0, 'PERCENT'), '100.0%');
+      });
+
+      it('should format WORK_DUR', function () {
+        assert.equal(window.formatMeasure(0, 'WORK_DUR'), '0');
+        assert.equal(window.formatMeasure(5 * ONE_DAY, 'WORK_DUR'), '5d');
+        assert.equal(window.formatMeasure(2 * ONE_HOUR, 'WORK_DUR'), '2h');
+        assert.equal(window.formatMeasure(ONE_MINUTE, 'WORK_DUR'), '1min');
+        assert.equal(window.formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR, 'WORK_DUR'), '5d 2h');
+        assert.equal(window.formatMeasure(2 * ONE_HOUR + ONE_MINUTE, 'WORK_DUR'), '2h 1min');
+        assert.equal(window.formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'WORK_DUR'), '5d 2h');
+        assert.equal(window.formatMeasure(15 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'WORK_DUR'), '15d');
+        assert.equal(window.formatMeasure(-5 * ONE_DAY, 'WORK_DUR'), '-5d');
+        assert.equal(window.formatMeasure(-2 * ONE_HOUR, 'WORK_DUR'), '-2h');
+        assert.equal(window.formatMeasure(-1 * ONE_MINUTE, 'WORK_DUR'), '-1min');
+      });
+
+      it('should format SHORT_WORK_DUR', function () {
+        assert.equal(window.formatMeasure(0, 'SHORT_WORK_DUR'), '0');
+        assert.equal(window.formatMeasure(5 * ONE_DAY, 'SHORT_WORK_DUR'), '5d');
+        assert.equal(window.formatMeasure(2 * ONE_HOUR, 'SHORT_WORK_DUR'), '2h');
+        assert.equal(window.formatMeasure(ONE_MINUTE, 'SHORT_WORK_DUR'), '1min');
+        assert.equal(window.formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR, 'SHORT_WORK_DUR'), '~ 5d');
+        assert.equal(window.formatMeasure(2 * ONE_HOUR + ONE_MINUTE, 'SHORT_WORK_DUR'), '~ 2h');
+        assert.equal(window.formatMeasure(5 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'SHORT_WORK_DUR'), '~ 5d');
+        assert.equal(window.formatMeasure(15 * ONE_DAY + 2 * ONE_HOUR + ONE_MINUTE, 'SHORT_WORK_DUR'), '~ 15d');
+        assert.equal(window.formatMeasure(7 * ONE_MINUTE, 'SHORT_WORK_DUR'), '7min');
+        assert.equal(window.formatMeasure(-5 * ONE_DAY, 'SHORT_WORK_DUR'), '-5d');
+        assert.equal(window.formatMeasure(-2 * ONE_HOUR, 'SHORT_WORK_DUR'), '-2h');
+        assert.equal(window.formatMeasure(-1 * ONE_MINUTE, 'SHORT_WORK_DUR'), '-1min');
+
+        assert.equal(window.formatMeasure(1529 * ONE_DAY, 'SHORT_WORK_DUR'), '1.5kd');
+        assert.equal(window.formatMeasure(1234567 * ONE_DAY, 'SHORT_WORK_DUR'), '1md');
+        assert.equal(window.formatMeasure(1234567 * ONE_DAY + 2 * ONE_HOUR, 'SHORT_WORK_DUR'), '1md');
+      });
+
+      it('should format RATING', function () {
+        assert.equal(window.formatMeasure(1, 'RATING'), 'A');
+        assert.equal(window.formatMeasure(2, 'RATING'), 'B');
+        assert.equal(window.formatMeasure(3, 'RATING'), 'C');
+        assert.equal(window.formatMeasure(4, 'RATING'), 'D');
+        assert.equal(window.formatMeasure(5, 'RATING'), 'E');
+      });
+
+      it('should not format unknown type', function () {
+        assert.equal(window.formatMeasure('random value', 'RANDOM_TYPE'), 'random value');
+      });
+
+      it('should not fail without parameters', function () {
+        assert.isNull(window.formatMeasure());
+      });
+    });
+
+    describe('#formatMeasureVariation()', function () {
+      it('should format INT', function () {
+        assert.equal(window.formatMeasureVariation(0, 'INT'), '0');
+        assert.equal(window.formatMeasureVariation(1, 'INT'), '+1');
+        assert.equal(window.formatMeasureVariation(-1, 'INT'), '-1');
+        assert.equal(window.formatMeasureVariation(1529, 'INT'), '+1,529');
+        assert.equal(window.formatMeasureVariation(-1529, 'INT'), '-1,529');
+      });
+
+      it('should format SHORT_INT', function () {
+        assert.equal(window.formatMeasureVariation(0, 'SHORT_INT'), '0');
+        assert.equal(window.formatMeasureVariation(1, 'SHORT_INT'), '+1');
+        assert.equal(window.formatMeasureVariation(-1, 'SHORT_INT'), '-1');
+        assert.equal(window.formatMeasureVariation(1529, 'SHORT_INT'), '+1.5k');
+        assert.equal(window.formatMeasureVariation(-1529, 'SHORT_INT'), '-1.5k');
+        assert.equal(window.formatMeasureVariation(10678, 'SHORT_INT'), '+11k');
+        assert.equal(window.formatMeasureVariation(-10678, 'SHORT_INT'), '-11k');
+      });
+
+      it('should format FLOAT', function () {
+        assert.equal(window.formatMeasureVariation(0.0, 'FLOAT'), '0');
+        assert.equal(window.formatMeasureVariation(1.0, 'FLOAT'), '+1.0');
+        assert.equal(window.formatMeasureVariation(-1.0, 'FLOAT'), '-1.0');
+        assert.equal(window.formatMeasureVariation(50.89, 'FLOAT'), '+50.9');
+        assert.equal(window.formatMeasureVariation(-50.89, 'FLOAT'), '-50.9');
+      });
+
+      it('should format PERCENT', function () {
+        assert.equal(window.formatMeasureVariation(0.0, 'PERCENT'), '0%');
+        assert.equal(window.formatMeasureVariation(1.0, 'PERCENT'), '+1.0%');
+        assert.equal(window.formatMeasureVariation(-1.0, 'PERCENT'), '-1.0%');
+        assert.equal(window.formatMeasureVariation(50.89, 'PERCENT'), '+50.9%');
+        assert.equal(window.formatMeasureVariation(-50.89, 'PERCENT'), '-50.9%');
+      });
+
+      it('should format WORK_DUR', function () {
+        assert.equal(window.formatMeasureVariation(0, 'WORK_DUR'), '0');
+        assert.equal(window.formatMeasureVariation(5 * ONE_DAY, 'WORK_DUR'), '+5d');
+        assert.equal(window.formatMeasureVariation(2 * ONE_HOUR, 'WORK_DUR'), '+2h');
+        assert.equal(window.formatMeasureVariation(ONE_MINUTE, 'WORK_DUR'), '+1min');
+        assert.equal(window.formatMeasureVariation(-5 * ONE_DAY, 'WORK_DUR'), '-5d');
+        assert.equal(window.formatMeasureVariation(-2 * ONE_HOUR, 'WORK_DUR'), '-2h');
+        assert.equal(window.formatMeasureVariation(-1 * ONE_MINUTE, 'WORK_DUR'), '-1min');
+      });
+
+      it('should not format unknown type', function () {
+        assert.equal(window.formatMeasureVariation('random value', 'RANDOM_TYPE'), 'random value');
+      });
+
+      it('should not fail without parameters', function () {
+        assert.isNull(window.formatMeasureVariation());
+      });
+    });
+  });
+
+  describe('Severity Comparators', function () {
+    describe('#severityComparator', function () {
+      it('should have correct order', function () {
+        assert.equal(window.severityComparator('BLOCKER'), 0);
+        assert.equal(window.severityComparator('CRITICAL'), 1);
+        assert.equal(window.severityComparator('MAJOR'), 2);
+        assert.equal(window.severityComparator('MINOR'), 3);
+        assert.equal(window.severityComparator('INFO'), 4);
+      });
+    });
+
+    describe('#severityColumnsComparator', function () {
+      it('should have correct order', function () {
+        assert.equal(window.severityColumnsComparator('BLOCKER'), 0);
+        assert.equal(window.severityColumnsComparator('CRITICAL'), 2);
+        assert.equal(window.severityColumnsComparator('MAJOR'), 4);
+        assert.equal(window.severityColumnsComparator('MINOR'), 1);
+        assert.equal(window.severityColumnsComparator('INFO'), 3);
+      });
+    });
+  });
+});
diff --git a/server/sonar-web/tests/apps/background-tasks-test.js b/server/sonar-web/tests/apps/background-tasks-test.js
new file mode 100644 (file)
index 0000000..d9588cc
--- /dev/null
@@ -0,0 +1,35 @@
+import React from 'react/addons';
+import App from '../../src/main/js/apps/background-tasks/app';
+import Header from '../../src/main/js/apps/background-tasks/header';
+import {STATUSES, CURRENTS} from '../../src/main/js/apps/background-tasks/constants';
+
+let TestUtils = React.addons.TestUtils;
+let expect = require('chai').expect;
+
+describe('Background Tasks', function () {
+  describe('App', () => {
+    it('should have #start()', () => {
+      expect(App.start).to.be.a('function');
+    });
+  });
+
+  describe('Constants', () => {
+    it('should have STATUSES', () => {
+      expect(STATUSES).to.be.a('object');
+      expect(Object.keys(STATUSES).length).to.equal(6);
+    });
+
+    it('should have CURRENTS', () => {
+      expect(CURRENTS).to.be.a('object');
+      expect(Object.keys(CURRENTS).length).to.equal(2);
+    });
+  });
+
+  describe('Header', () => {
+    it('should render', () => {
+      let component = TestUtils.renderIntoDocument(<Header/>),
+          header = TestUtils.scryRenderedDOMComponentsWithTag(component, 'header');
+      expect(header.length).to.equal(1);
+    });
+  });
+});
diff --git a/server/sonar-web/tests/apps/nav-test.js b/server/sonar-web/tests/apps/nav-test.js
new file mode 100644 (file)
index 0000000..d31802e
--- /dev/null
@@ -0,0 +1,25 @@
+import React from 'react/addons';
+import ComponentNavBreadcrumbs from '../../src/main/js/apps/nav/component/component-nav-breadcrumbs';
+
+let TestUtils = React.addons.TestUtils;
+let expect = require('chai').expect;
+
+describe('Nav', function () {
+  describe('ComponentNavBreadcrumbs', () => {
+    it('should not render unless `props.breadcrumbs`', function () {
+      var result = React.renderToStaticMarkup(React.createElement(ComponentNavBreadcrumbs, null));
+      expect(result).to.equal('<noscript></noscript>');
+    });
+
+    it('should not render breadcrumbs with one element', function () {
+      var breadcrumbs = [
+        { key: 'my-project', name: 'My Project', qualifier: 'TRK' }
+      ];
+      var result = TestUtils.renderIntoDocument(
+          React.createElement(ComponentNavBreadcrumbs, { breadcrumbs: breadcrumbs })
+      );
+      expect(TestUtils.scryRenderedDOMComponentsWithTag(result, 'li')).to.have.length(1);
+      expect(TestUtils.scryRenderedDOMComponentsWithTag(result, 'a')).to.have.length(1);
+    });
+  });
+});
diff --git a/server/sonar-web/tests/components/issue-test.js b/server/sonar-web/tests/components/issue-test.js
new file mode 100644 (file)
index 0000000..34b0312
--- /dev/null
@@ -0,0 +1,173 @@
+import Issue from '../../src/main/js/components/issue/models/issue';
+
+let sinon = require('sinon'),
+    sinonChai = require('sinon-chai'),
+    chai = require('chai'),
+    expect = chai.expect;
+
+chai.use(sinonChai);
+
+describe('Issue', function () {
+  describe('Model', function () {
+    it('should have correct urlRoot', function () {
+      var issue = new Issue();
+      expect(issue.urlRoot()).to.equal('/api/issues');
+    });
+
+    it('should parse response without root issue object', function () {
+      var issue = new Issue();
+      var example = { a: 1 };
+      expect(issue.parse(example)).to.deep.equal(example);
+    });
+
+    it('should parse response with the root issue object', function () {
+      var issue = new Issue();
+      var example = { a: 1 };
+      expect(issue.parse({ issue: example })).to.deep.equal(example);
+    });
+
+    it('should reset attributes (no attributes initially)', function () {
+      var issue = new Issue();
+      var example = { a: 1 };
+      issue.reset(example);
+      expect(issue.toJSON()).to.deep.equal(example);
+    });
+
+    it('should reset attributes (override attribute)', function () {
+      var issue = new Issue({ a: 2 });
+      var example = { a: 1 };
+      issue.reset(example);
+      expect(issue.toJSON()).to.deep.equal(example);
+    });
+
+    it('should reset attributes (different attributes)', function () {
+      var issue = new Issue({ a: 2 });
+      var example = { b: 1 };
+      issue.reset(example);
+      expect(issue.toJSON()).to.deep.equal(example);
+    });
+
+    it('should unset `textRange` of a closed issue', function () {
+      var issue = new Issue();
+      var result = issue.parse({ issue: { status: 'CLOSED', textRange: { startLine: 5 } } });
+      expect(result.textRange).to.not.be.ok;
+    });
+
+    it('should unset `flows` of a closed issue', function () {
+      var issue = new Issue();
+      var result = issue.parse({ issue: { status: 'CLOSED', flows: [1, 2, 3] } });
+      expect(result.flows).to.deep.equal([]);
+    });
+
+    describe('Actions', function () {
+      it('should assign', function () {
+        var issue = new Issue({ key: 'issue-key' });
+        var spy = sinon.spy();
+        issue._action = spy;
+        issue.assign('admin');
+        expect(spy).to.have.been.calledWith({
+          data: { assignee: 'admin', issue: 'issue-key' },
+          url: '/api/issues/assign'
+        });
+      });
+
+      it('should unassign', function () {
+        var issue = new Issue({ key: 'issue-key' });
+        var spy = sinon.spy();
+        issue._action = spy;
+        issue.assign();
+        expect(spy).to.have.been.calledWith({
+          data: { assignee: undefined, issue: 'issue-key' },
+          url: '/api/issues/assign'
+        });
+      });
+
+      it('should plan', function () {
+        var issue = new Issue({ key: 'issue-key' });
+        var spy = sinon.spy();
+        issue._action = spy;
+        issue.plan('plan');
+        expect(spy).to.have.been.calledWith({ data: { plan: 'plan', issue: 'issue-key' }, url: '/api/issues/plan' });
+      });
+
+      it('should unplan', function () {
+        var issue = new Issue({ key: 'issue-key' });
+        var spy = sinon.spy();
+        issue._action = spy;
+        issue.plan();
+        expect(spy).to.have.been.calledWith({ data: { plan: undefined, issue: 'issue-key' }, url: '/api/issues/plan' });
+      });
+
+      it('should set severity', function () {
+        var issue = new Issue({ key: 'issue-key' });
+        var spy = sinon.spy();
+        issue._action = spy;
+        issue.setSeverity('BLOCKER');
+        expect(spy).to.have.been.calledWith({
+          data: { severity: 'BLOCKER', issue: 'issue-key' },
+          url: '/api/issues/set_severity'
+        });
+      });
+    });
+
+    describe('#getLinearLocations', function () {
+      it('should return single line location', function () {
+        var issue = new Issue({ textRange: { startLine: 1, endLine: 1, startOffset: 0, endOffset: 10 } }),
+            locations = issue.getLinearLocations();
+        expect(locations.length).to.equal(1);
+
+        expect(locations[0].line).to.equal(1);
+        expect(locations[0].from).to.equal(0);
+        expect(locations[0].to).to.equal(10);
+      });
+
+      it('should return location not from 0', function () {
+        var issue = new Issue({ textRange: { startLine: 1, endLine: 1, startOffset: 5, endOffset: 10 } }),
+            locations = issue.getLinearLocations();
+        expect(locations.length).to.equal(1);
+
+        expect(locations[0].line).to.equal(1);
+        expect(locations[0].from).to.equal(5);
+        expect(locations[0].to).to.equal(10);
+      });
+
+      it('should return 2-lines location', function () {
+        var issue = new Issue({ textRange: { startLine: 2, endLine: 3, startOffset: 5, endOffset: 10 } }),
+            locations = issue.getLinearLocations();
+        expect(locations.length).to.equal(2);
+
+        expect(locations[0].line).to.equal(2);
+        expect(locations[0].from).to.equal(5);
+        expect(locations[0].to).to.equal(999999);
+
+        expect(locations[1].line).to.equal(3);
+        expect(locations[1].from).to.equal(0);
+        expect(locations[1].to).to.equal(10);
+      });
+
+      it('should return 3-lines location', function () {
+        var issue = new Issue({ textRange: { startLine: 4, endLine: 6, startOffset: 5, endOffset: 10 } }),
+            locations = issue.getLinearLocations();
+        expect(locations.length).to.equal(3);
+
+        expect(locations[0].line).to.equal(4);
+        expect(locations[0].from).to.equal(5);
+        expect(locations[0].to).to.equal(999999);
+
+        expect(locations[1].line).to.equal(5);
+        expect(locations[1].from).to.equal(0);
+        expect(locations[1].to).to.equal(999999);
+
+        expect(locations[2].line).to.equal(6);
+        expect(locations[2].from).to.equal(0);
+        expect(locations[2].to).to.equal(10);
+      });
+
+      it('should return [] when no location', function () {
+        var issue = new Issue(),
+            locations = issue.getLinearLocations();
+        expect(locations.length).to.equal(0);
+      });
+    });
+  });
+});
diff --git a/server/sonar-web/tests/components/source-viewer-test.js b/server/sonar-web/tests/components/source-viewer-test.js
new file mode 100644 (file)
index 0000000..abdad6b
--- /dev/null
@@ -0,0 +1,76 @@
+import helper from '../../src/main/js/components/source-viewer/helpers/code-with-issue-locations-helper';
+
+let expect = require('chai').expect;
+
+describe('Source Viewer', function () {
+  describe('Code With Issue Locations Helper', function () {
+    it('should be a function', function () {
+      expect(helper).to.be.a('function');
+    });
+
+    it('should mark one location', function () {
+      var code = '<span class="k">if</span> (<span class="sym-2 sym">a</span> + <span class="c">1</span>) {',
+          locations = [{ from: 1, to: 5 }],
+          result = helper(code, locations, 'x');
+      expect(result).to.equal([
+        '<span class="k">i</span>',
+        '<span class="k x">f</span>',
+        '<span class=" x"> (</span>',
+        '<span class="sym-2 sym x">a</span>',
+        '<span class=""> + </span>',
+        '<span class="c">1</span>',
+        '<span class="">) {</span>'
+      ].join(''));
+    });
+
+    it('should mark two locations', function () {
+      var code = 'abcdefghijklmnopqrst',
+          locations = [
+            { from: 1, to: 6 },
+            { from: 11, to: 16 }
+          ],
+          result = helper(code, locations, 'x');
+      expect(result).to.equal([
+        '<span class="">a</span>',
+        '<span class=" x">bcdef</span>',
+        '<span class="">ghijk</span>',
+        '<span class=" x">lmnop</span>',
+        '<span class="">qrst</span>'
+      ].join(''));
+    });
+
+    it('should mark one locations', function () {
+      var code = '<span class="cppd"> * Copyright (C) 2008-2014 SonarSource</span>',
+          locations = [{ from: 15, to: 20 }],
+          result = helper(code, locations, 'x');
+      expect(result).to.equal([
+        '<span class="cppd"> * Copyright (C</span>',
+        '<span class="cppd x">) 200</span>',
+        '<span class="cppd">8-2014 SonarSource</span>'
+      ].join(''));
+    });
+
+    it('should mark two locations', function () {
+      var code = '<span class="cppd"> * Copyright (C) 2008-2014 SonarSource</span>',
+          locations = [
+            { from: 24, to: 29 },
+            { from: 15, to: 20 }
+          ],
+          result = helper(code, locations, 'x');
+      expect(result).to.equal([
+        '<span class="cppd"> * Copyright (C</span>',
+        '<span class="cppd x">) 200</span>',
+        '<span class="cppd">8-20</span>',
+        '<span class="cppd x">14 So</span>',
+        '<span class="cppd">narSource</span>'
+      ].join(''));
+    });
+
+    it('should parse line with < and >', function () {
+      var code = '<span class="j">#include &lt;stdio.h&gt;</span>',
+          result = helper(code, []);
+      expect(result).to.equal('<span class="j">#include &lt;stdio.h&gt;</span>');
+    });
+  });
+});
+
diff --git a/server/sonar-web/tests/jsdom-setup.js b/server/sonar-web/tests/jsdom-setup.js
new file mode 100644 (file)
index 0000000..ade0fc3
--- /dev/null
@@ -0,0 +1,9 @@
+var jsdom = require('jsdom');
+
+// A super simple DOM ready for React to render into
+// Store this DOM and the window in global scope ready for React to access
+global.document = jsdom.jsdom('<!doctype html><html><body></body></html>');
+global.window = document.defaultView;
+global.navigator = document.defaultView.navigator;
+
+global.window.baseUrl = '';
diff --git a/server/sonar-web/tests/mocha.opts b/server/sonar-web/tests/mocha.opts
new file mode 100644 (file)
index 0000000..94ed838
--- /dev/null
@@ -0,0 +1,4 @@
+--recursive
+--compilers js:babel/register
+--compilers jsx:babel/register
+--require tests/jsdom-setup.js
index 062d09a0dd8cf4a9c217b5171a32c1aa22a14f6d..36081781f97c2b51d9f1b4d934cc3c1ad0db5995 100755 (executable)
--- a/travis.sh
+++ b/travis.sh
@@ -34,13 +34,8 @@ MYSQL)
   ;;
 
 WEB)
-  installTravisTools
-
-  /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16
-  wget http://selenium-release.storage.googleapis.com/2.46/selenium-server-standalone-2.46.0.jar
-  nohup java -jar selenium-server-standalone-2.46.0.jar &
-  sleep 3
-
+  set +eu
+  source ~/.nvm/nvm.sh && nvm install 4
   cd server/sonar-web && npm install && npm test
   ;;