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..30307ca383 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,100 @@ +module.exports = function(grunt) { + + var jsPath = "app/assets/javascripts/"; + var testSource = "mocha/index.html"; + // Project configuration. + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + jshint: { + all: { + files: { + src: [jsPath] + } + } + }, + mocha_phantomjs: { + 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: [jsPath] , + tasks: ['jshint', 'mocha_phantomjs:small'], + options: { + spawn: false, + }, + }, + }, + }); + + grunt.loadNpmTasks("grunt-jscoverage"); + grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-mocha-phantomjs'); + + // 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); + + + fs.renameSync(tempPath + "date-en-US.js", jsPath + "date-en-US.js"); + }); + + 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/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, diff --git a/doc/CHANGELOG.md b/doc/CHANGELOG.md index 840a535ceb..330a7b66a0 100644 --- a/doc/CHANGELOG.md +++ b/doc/CHANGELOG.md @@ -49,6 +49,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 diff --git a/package.json b/package.json new file mode 100644 index 0000000000..9b6cc6a1cd --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "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-jscoverage": "0.0.3", + "rimraf": "~2.2.5", + "json2htmlcov": "~0.1.1", + "exec": "0.0.6" + } +}