From 1671d66d22acf9cf633cd64705dd427c3546ac91 Mon Sep 17 00:00:00 2001 From: Nils Kenneweg Date: Mon, 13 Jan 2014 16:22:06 +0100 Subject: [PATCH 1/5] fix all the jshint errors. --- app/assets/javascripts/members_select_boxes.js | 14 +++++++------- app/assets/javascripts/timelines.js | 2 +- app/assets/javascripts/timelines/model/Color.js | 2 +- .../javascripts/timelines/model/PlanningElement.js | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/assets/javascripts/members_select_boxes.js b/app/assets/javascripts/members_select_boxes.js index 41220a7f00..c079284dc1 100644 --- a/app/assets/javascripts/members_select_boxes.js +++ b/app/assets/javascripts/members_select_boxes.js @@ -48,11 +48,11 @@ jQuery(document).ready(function($) { markup.push(OpenProject.Helpers.markupEscape( item.name.substring(match + tl, item.name.length))); return markup.join(""); - } + }; formatItemSelection = function (item) { return OpenProject.Helpers.markupEscape(item.name); - } + }; $("#members_add_form select.select2-select").each(function (ix, elem){ if ($(elem).hasClass("remote") || $(elem).attr("data-ajaxURL") !== undefined) { @@ -60,12 +60,12 @@ jQuery(document).ready(function($) { if (!$.isEmptyObject(elem.siblings('div.select2-select.select2-container'))) { setTimeout (function () { var attributes, allowed, currentName, fakeInput; - attributes = {} + attributes = {}; allowed = ["title", "placeholder"]; for(var i = 0; i < $(elem).get(0).attributes.length; i++) { currentName = $(elem).get(0).attributes[i].name; - if(currentName.indexOf("data-") == 0 || $.inArray(currentName, allowed)); //only ones starting with data- + if(currentName.indexOf("data-") === 0 || $.inArray(currentName, allowed)); //only ones starting with data- attributes[currentName] = $(elem).attr(currentName); } fakeInput = $(elem).after("").siblings(":input:first"); @@ -88,7 +88,7 @@ jQuery(document).ready(function($) { }, results: function (data, page) { - active_items = [] + active_items = []; data.results.items.each(function (e) { e.name = $('
').text(e.name).html();
                         active_items.push(e);
@@ -107,10 +107,10 @@ jQuery(document).ready(function($) {
         $(elem).select2();
       }
     });
-  }
+  };
 
   memberstab = $('#tab-members').first();
-  if ((memberstab != null) && (memberstab.hasClass("selected"))) {
+  if ((memberstab !== null) && (memberstab.hasClass("selected"))) {
     init_members_cb();
   } else {
     memberstab.click(init_members_cb);
diff --git a/app/assets/javascripts/timelines.js b/app/assets/javascripts/timelines.js
index 78bd206d19..cbdd857ff7 100644
--- a/app/assets/javascripts/timelines.js
+++ b/app/assets/javascripts/timelines.js
@@ -73,7 +73,7 @@
 
 // environment and other global vars
 /*jshint browser:true, devel:true*/
-/*global jQuery:false, Raphael:false, Timeline:true*/
+/*global jQuery:false, Raphael:false, Timeline:true, modalHelperInstance: true, I18n: true*/
 
 if (typeof Timeline === "undefined") {
   Timeline = {};
diff --git a/app/assets/javascripts/timelines/model/Color.js b/app/assets/javascripts/timelines/model/Color.js
index a2f1cf489d..f76e33196f 100644
--- a/app/assets/javascripts/timelines/model/Color.js
+++ b/app/assets/javascripts/timelines/model/Color.js
@@ -1,4 +1,4 @@
-7//-- copyright
+//-- copyright
 // OpenProject is a project management system.
 // Copyright (C) 2012-2013 the OpenProject Foundation (OPF)
 //
diff --git a/app/assets/javascripts/timelines/model/PlanningElement.js b/app/assets/javascripts/timelines/model/PlanningElement.js
index 8a6848fac3..e1135cf1c0 100644
--- a/app/assets/javascripts/timelines/model/PlanningElement.js
+++ b/app/assets/javascripts/timelines/model/PlanningElement.js
@@ -672,7 +672,7 @@ Timeline.PlanningElement = {
           textColor = timeline.getLimunanceFor(color) > Timeline.PE_LUMINANCE_THRESHOLD ?
                       Timeline.PE_DARK_TEXT_COLOR : Timeline.PE_LIGHT_TEXT_COLOR;
 
-          var text = this.subject;
+          text = this.subject;
           label = timeline.paper.text(0, 0, text);
           label.attr({
             'font-size': 12,

From 0cb27b90908ba019b06f3fe5bec7eaa60c652950 Mon Sep 17 00:00:00 2001
From: Nils Kenneweg 
Date: Mon, 13 Jan 2014 16:23:38 +0100
Subject: [PATCH 2/5] add package.json gruntfile and jshintignore.

---
 .gitignore    |  1 +
 .jshintignore | 24 ++++++++++++++++++++++++
 Gruntfile.js  | 37 +++++++++++++++++++++++++++++++++++++
 package.json  | 13 +++++++++++++
 4 files changed, 75 insertions(+)
 create mode 100644 .jshintignore
 create mode 100644 Gruntfile.js
 create mode 100644 package.json

diff --git a/.gitignore b/.gitignore
index fa7ccf614b..5e95d2224f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -79,3 +79,4 @@
 
 # asset cache
 /.sass-cache/
+/node_modules/
diff --git a/.jshintignore b/.jshintignore
new file mode 100644
index 0000000000..55a546e48d
--- /dev/null
+++ b/.jshintignore
@@ -0,0 +1,24 @@
+app/assets/javascripts/raphael.js
+app/assets/javascripts/raphael-min.js
+app/assets/javascripts/date-de-DE.js
+app/assets/javascripts/date-en-US.js
+app/assets/javascripts/jstoolbar/**/*
+app/assets/javascripts/jquery_noconflict.js
+app/assets/javascripts/pages/**/*
+app/assets/javascripts/project/**/*
+app/assets/javascripts/jquery.menu_expand.js
+app/assets/javascripts/jquery-ui-i18n.js
+
+#we sould fix this ones
+app/assets/javascripts/select_list_move.js
+app/assets/javascripts/context_menu.js
+app/assets/javascripts/breadcrumb.js
+app/assets/javascripts/keyboard_shortcuts.js
+app/assets/javascripts/openproject.js
+app/assets/javascripts/top-shelf.js
+app/assets/javascripts/top_menu.js
+app/assets/javascripts/findDomElement.js
+app/assets/javascripts/application.js
+app/assets/javascripts/action_menu.js
+app/assets/javascripts/accessibility.js
+app/assets/javascripts/repository_navigation.js
diff --git a/Gruntfile.js b/Gruntfile.js
new file mode 100644
index 0000000000..80550c4751
--- /dev/null
+++ b/Gruntfile.js
@@ -0,0 +1,37 @@
+module.exports = function(grunt) {
+
+  var jsPath = "app/assets/javascripts/";
+  var jsPaths = ['timelines/**/*.js', 'timelines.js', 'timelines_modal.js', 'timelines_select_boxes.js', 'members_form.js', 'members_select_boxes.js'].map(function (e) { return jsPath + e; });
+
+  // Project configuration.
+  grunt.initConfig({
+    pkg: grunt.file.readJSON('package.json'),
+    jshint: {
+      all: {
+        files: {
+          src: ["app/assets/javascripts/"] //jsPaths
+        }
+      }
+    },
+    mocha_phantomjs: {
+      all: ['mocha/index.html']
+    },
+    watch: {
+      scripts: {
+        files: jsPaths,
+        tasks: ['jshint', 'mocha_phantomjs'],
+        options: {
+          spawn: false,
+        },
+      },
+    },
+  });
+
+  grunt.loadNpmTasks('grunt-contrib-jshint');
+  grunt.loadNpmTasks('grunt-contrib-watch');
+  grunt.loadNpmTasks('grunt-mocha-phantomjs');
+
+  // Default task(s).
+  grunt.registerTask('default', ['jshint']);
+
+};
\ No newline at end of file
diff --git a/package.json b/package.json
new file mode 100644
index 0000000000..4fb24d98b7
--- /dev/null
+++ b/package.json
@@ -0,0 +1,13 @@
+{
+  "name": "OpenProject",
+  "version": "0.1.0",
+  "devDependencies": {
+    "grunt": "~0.4.2",
+    "grunt-contrib-jshint": "~0.8.0",
+    "mocha-phantomjs": "~3.1.6",
+    "phantomjs": "~1.9.2",
+    "grunt-contrib-watch": "~0.5.3",
+    "grunt-mocha-phantomjs": "~0.3.2",
+    "grunt-mocha": "~0.4.8"
+  }
+}

From 72be0d3347ad7e428424e0a19a5fa1abd7adada0 Mon Sep 17 00:00:00 2001
From: Nils Kenneweg 
Date: Mon, 13 Jan 2014 18:19:25 +0100
Subject: [PATCH 3/5] more advanced gruntfile.

---
 Gruntfile.js | 72 +++++++++++++++++++++++++++++++++++++++++++++++-----
 package.json |  5 +++-
 2 files changed, 70 insertions(+), 7 deletions(-)

diff --git a/Gruntfile.js b/Gruntfile.js
index 80550c4751..88d5ba85ff 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -1,25 +1,49 @@
 module.exports = function(grunt) {
 
   var jsPath = "app/assets/javascripts/";
-  var jsPaths = ['timelines/**/*.js', 'timelines.js', 'timelines_modal.js', 'timelines_select_boxes.js', 'members_form.js', 'members_select_boxes.js'].map(function (e) { return jsPath + e; });
-
+  var testSource = "mocha/index.html";
   // Project configuration.
   grunt.initConfig({
     pkg: grunt.file.readJSON('package.json'),
     jshint: {
       all: {
         files: {
-          src: ["app/assets/javascripts/"] //jsPaths
+          src: [jsPath]
         }
       }
     },
     mocha_phantomjs: {
-      all: ['mocha/index.html']
+      all: [testSource],
+      small: {
+        src:[testSource],
+        options: {
+          reporter: "dot"
+        }
+      },
+      cov: {
+        src:[testSource],
+        options: {
+          output: "coverage.json",
+          reporter: "json-cov"
+        }
+      },
+      jenkins: {
+        src:[testSource],
+        options: {
+          reporter: "XUnit"
+        }
+      }
+    },
+    jscoverage: {
+      options: {
+        inputDirectory: 'app/assets/javascripts/',
+        outputDirectory: 'app/assets/javascripts_cov/'
+      }
     },
     watch: {
       scripts: {
-        files: jsPaths,
-        tasks: ['jshint', 'mocha_phantomjs'],
+        files: [jsPath] ,
+        tasks: ['jshint', 'mocha_phantomjs:small'],
         options: {
           spawn: false,
         },
@@ -27,6 +51,7 @@ module.exports = function(grunt) {
     },
   });
 
+  grunt.loadNpmTasks("grunt-jscoverage");
   grunt.loadNpmTasks('grunt-contrib-jshint');
   grunt.loadNpmTasks('grunt-contrib-watch');
   grunt.loadNpmTasks('grunt-mocha-phantomjs');
@@ -34,4 +59,39 @@ module.exports = function(grunt) {
   // Default task(s).
   grunt.registerTask('default', ['jshint']);
 
+  var tempPath = "app/assets/javascripts_temp/";
+  var covPath = "app/assets/javascripts_cov/";
+
+  grunt.registerTask('moveFiles', 'switch directories for coverage-enabled files and normal files', function () {
+    var fs = require("fs");
+    fs.renameSync(jsPath, tempPath);
+    fs.renameSync(covPath, jsPath);
+  });
+
+  grunt.registerTask('cleanUpCoverage', 'undo moveFiles', function () {
+    var done = this.async();
+
+    var fs = require("fs");
+    if (fs.existsSync(tempPath)) {
+      fs.renameSync(jsPath, covPath);
+      fs.renameSync(tempPath, jsPath);
+    }
+
+    if (fs.existsSync("coverage.json")) {
+      fs.unlinkSync("coverage.json");
+    }
+
+    rmdir = require("rimraf");
+    rmdir(covPath, function (e) {
+        done(e);
+    });
+  });
+
+  grunt.registerTask('jsonCov2Html', 'convert coverage to html', function () {
+    var exec = require("exec");
+    exec("cat coverage.json | node_modules/json2htmlcov/bin/json2htmlcov > coverage.html");
+  });
+
+  grunt.registerTask('coverage', ['jscoverage', 'moveFiles', 'mocha_phantomjs:cov', 'jsonCov2Html', 'cleanUpCoverage']);
+
 };
\ No newline at end of file
diff --git a/package.json b/package.json
index 4fb24d98b7..9b6cc6a1cd 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,9 @@
     "phantomjs": "~1.9.2",
     "grunt-contrib-watch": "~0.5.3",
     "grunt-mocha-phantomjs": "~0.3.2",
-    "grunt-mocha": "~0.4.8"
+    "grunt-jscoverage": "0.0.3",
+    "rimraf": "~2.2.5",
+    "json2htmlcov": "~0.1.1",
+    "exec": "0.0.6"
   }
 }

From 7a6dd7bc15e524fd56ea64cdaacdfe61717f72a7 Mon Sep 17 00:00:00 2001
From: Nils Kenneweg 
Date: Fri, 17 Jan 2014 10:04:10 +0100
Subject: [PATCH 4/5] ignore certain files in coverage.

---
 Gruntfile.js | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/Gruntfile.js b/Gruntfile.js
index 88d5ba85ff..30307ca383 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -66,6 +66,9 @@ module.exports = function(grunt) {
     var fs = require("fs");
     fs.renameSync(jsPath, tempPath);
     fs.renameSync(covPath, jsPath);
+
+
+    fs.renameSync(tempPath + "date-en-US.js", jsPath + "date-en-US.js");
   });
 
   grunt.registerTask('cleanUpCoverage', 'undo moveFiles', function () {

From eb5176ebb530482b784bd9119dea71b1a8eaf381 Mon Sep 17 00:00:00 2001
From: Martin Czuchra 
Date: Mon, 3 Feb 2014 16:49:47 +0100
Subject: [PATCH 5/5] Changelog.

---
 doc/CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md
index 2e50ae7f4d..cdb58ffc3e 100644
--- a/doc/CHANGELOG.md
+++ b/doc/CHANGELOG.md
@@ -47,6 +47,7 @@ See doc/COPYRIGHT.rdoc for more details.
 * `#3854` Move function and Query filters allows to select groups as responsible
 * `#3974` [Timelines] Typo at creating timelines
 * `#4023` [Accessibility] Keep keyboard focus within modal while it's open
+* Add Gruntfile for easier JavaScript testing.
 
 ## 3.0.0pre43