From 537d25460d1d0b9b3b850689e71bb343dfa7ebe5 Mon Sep 17 00:00:00 2001 From: Andrew Rowls Date: Wed, 15 Jan 2014 01:38:52 -0500 Subject: [PATCH] Add basic npm support and Gruntfile, move tests to grunt --- .gitignore | 4 +- .hgignore | 4 +- .travis.yml | 12 ++-- Gruntfile.js | 93 +++++++++++++++++++++++++ README.md | 9 +++ package.json | 16 +++++ tests/README.md | 33 ++------- tests/_coverage.html | 26 ------- tests/run-qunit.js | 157 ------------------------------------------- tests/tests.html | 1 - 10 files changed, 130 insertions(+), 225 deletions(-) create mode 100644 Gruntfile.js create mode 100644 package.json delete mode 100644 tests/_coverage.html delete mode 100644 tests/run-qunit.js diff --git a/.gitignore b/.gitignore index 3a0d5cd79..fc4025729 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ -instrumented/ -tests/coverage.html docs/_build +node_modules/ +_build diff --git a/.hgignore b/.hgignore index 3a0d5cd79..8588fef6d 100644 --- a/.hgignore +++ b/.hgignore @@ -1,3 +1,3 @@ -instrumented/ -tests/coverage.html docs/_build +node_modules/ +_build/ diff --git a/.travis.yml b/.travis.yml index ce6df3b4a..10e48a5bc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,5 @@ +language: node_js +node_js: + - "0.10" install: - - npm install -g jshint jscs -before_script: - - cd ./tests - - echo "new Date().toString();" | phantomjs -script: - - jshint ../js/bootstrap-datepicker.js ../js/locales/*.js - - jscs -c ../.jscs.json ../js/bootstrap-datepicker.js ../js/locales/*.js - - phantomjs run-qunit.js tests.html + - npm install -g grunt-cli diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 000000000..e3c958682 --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,93 @@ +/* global module, require */ +module.exports = function(grunt){ + require('load-grunt-tasks')(grunt); + + grunt.initConfig({ + pkg: grunt.file.readJSON('package.json'), + qunit: { + all: ['tests/tests.html'] + }, + jshint: { + options: { + jshintrc: true + }, + gruntfile: ['Gruntfile.js'], + main: ['js/bootstrap-datepicker.js'], + locales: ['js/locales/*js'] + }, + jscs: { + /* grunt-contrib-jscs notes: + 0.1.2 works + 0.1.3 inifite loops on postinstall + 0.1.4 doesn't seem to hit all targets when run "grunt jscs" + */ + gruntfile: ['Gruntfile.js'], + main: ['js/bootstrap-datepicker.js'], + locales: ['js/locales/*js'] + }, + less: { + standalone: { + files: { + '_build/datepicker.standalone.css': 'build/build_standalone.less', + '_build/datepicker3.standalone.css': 'build/build_standalone3.less' + } + }, + css: { + files: { + '_build/datepicker.css': 'build/build.less', + '_build/datepicker3.css': 'build/build3.less' + } + }, + repo: { + files: { + 'css/datepicker.css': 'build/build_standalone.less', + 'css/datepicker3.css': 'build/build_standalone3.less' + } + } + }, + uglify: { + options: { + compress: true, + mangle: true + }, + main: { + options: { + sourceMap: function(dest){ + return dest.replace('.min.js', '.js.map'); + } + }, + files: { + '_build/bootstrap-datepicker.min.js': 'js/bootstrap-datepicker.js', + '_build/bootstrap-datepicker.locales.min.js': 'js/locales/*.js' + } + }, + locales: { + files: [{ + expand: true, + cwd: 'js/locales/', + src: ['*.js', '!*.min.js'], + dest: '_build/locales/', + rename: function(dest, name){ + return dest + name.replace(/\.js$/, '.min.js'); + } + }] + } + }, + cssmin: { + all: { + files: { + '_build/datepicker.standalone.min.css': '_build/datepicker.standalone.css', + '_build/datepicker.min.css': '_build/datepicker.css', + '_build/datepicker3.standalone.min.css': '_build/datepicker3.standalone.css', + '_build/datepicker3.min.css': '_build/datepicker3.css' + } + } + }, + clean: ['_build'] + }); + + grunt.registerTask('lint', 'Lint all js files with jshint and jscs', ['jshint', 'jscs']); + grunt.registerTask('test', 'Lint files and run unit tests', ['lint', 'qunit']); + grunt.registerTask('finish', 'Prepares repo for commit [test, less:repo]', ['test', 'less:repo']); + grunt.registerTask('dist', 'Builds minified files', ['less:css', 'less:standalone', 'cssmin', 'uglify']); +}; diff --git a/README.md b/README.md index 8218e24a9..05681bc01 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,16 @@ Please note that this fork is not used on Stefan's page, nor is it maintained or Versions are incremented according to [semver](http://semver.org/). +## Links + * [Online Demo](http://eternicode.github.io/bootstrap-datepicker/) * [Online Docs](http://bootstrap-datepicker.readthedocs.org/) (ReadTheDocs.com) * [Google Group](https://groups.google.com/group/bootstrap-datepicker/) * [Travis CI ![Build Status](https://travis-ci.org/eternicode/bootstrap-datepicker.png?branch=master)](https://travis-ci.org/eternicode/bootstrap-datepicker) + +## Development + +Once you cloned the repo, you'll need to install [grunt](http://gruntjs.com/) and the development dependencies using [npm](https://npmjs.org/). + + npm install -g grunt-cli + npm install diff --git a/package.json b/package.json new file mode 100644 index 000000000..7beebcc51 --- /dev/null +++ b/package.json @@ -0,0 +1,16 @@ +{ + "scripts": { + "test": "grunt test" + }, + "devDependencies": { + "grunt": "~0.4.2", + "load-grunt-tasks": "~0.2.1", + "grunt-contrib-jshint": "~0.8.0", + "grunt-contrib-jscs": "0.1.2", + "grunt-contrib-qunit": "~0.3.0", + "grunt-contrib-less": "~0.9.0", + "grunt-contrib-uglify": "~0.2.7", + "grunt-contrib-cssmin": "~0.7.0", + "grunt-contrib-clean": "~0.5.0" + } +} diff --git a/tests/README.md b/tests/README.md index 6f48f2bdd..47e29886a 100644 --- a/tests/README.md +++ b/tests/README.md @@ -7,30 +7,11 @@ bugs when adding new features and making changes. The simplest way to run the tests is to open `tests/tests.html` in your browser. The test suites will automatically run themselves and present their results. -To run the tests from the command line, download and install -[PhantomJS](http://phantomjs.org/), and run `run-qunit.js` with it: +To run the tests from the command line (after running jshint and jscs, which is +recommended), install Grunt and run the `test` task from anywhere within the +repo: - $ cd tests/ - $ phantomjs run-qunit.js tests.html - -Failed tests and their failed assertions will be printed to the console. A -results summary will be printed at the end. - -To generate coverage statistics, use [JSCoverage](http://siliconforks.com/jscoverage/) -to instrument the js files: - - $ cd tests/ - $ jscoverage ../js/ ../instrumented/ - $ phantomjs run-qunit.js tests.html - -Coverage percentage will be included in the output summary, and a highlighted -line-by-line html file will be generated. - -# Shout-out - -Thanks to Rod @ While One Fork for the -[CIS guide](http://whileonefork.blogspot.com/2011/10/integrating-javascript-tests-into-cli.html) -on putting the above together. + $ grunt test # Adding tests @@ -47,9 +28,3 @@ name and `` is the four-digit year the tests pertain to. In order for new tests to be run, they must be imported into `tests/tests.html`. Find the script includes headed by the html comment ``, and add a new one to the list which includes the new js files. - -# Can I use this? - -By all means, please do! Just note that I stopped working on this structure -once it fit my needs, there's no real support for it, and it may change in the -future. Otherwise, have at it. diff --git a/tests/_coverage.html b/tests/_coverage.html deleted file mode 100644 index 33d838de0..000000000 --- a/tests/_coverage.html +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - -COLORIZED_LINE_HTML - - - - diff --git a/tests/run-qunit.js b/tests/run-qunit.js deleted file mode 100644 index effdc3f10..000000000 --- a/tests/run-qunit.js +++ /dev/null @@ -1,157 +0,0 @@ -var system = require('system'); - -/** - * Wait until the test condition is true or a timeout occurs. Useful for waiting - * on a server response or for a ui change (fadeIn, etc.) to occur. - * - * @param testFx javascript condition that evaluates to a boolean, - * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or - * as a callback function. - * @param onReady what to do when testFx condition is fulfilled, - * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or - * as a callback function. - * @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used. - */ -function waitFor(testFx, onReady, timeOutMillis) { - var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 10001, //< Default Max Timout is 3s - start = new Date().getTime(), - condition = false, - interval = setInterval(function() { - if ( (new Date().getTime() - start < maxtimeOutMillis) && !condition ) { - // If not time-out yet and condition not yet fulfilled - condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code - } else { - if(!condition) { - // If condition still not fulfilled (timeout but condition is 'false') - console.log("'waitFor()' timeout"); - phantom.exit(1); - } else { - // Condition fulfilled (timeout and/or condition is 'true') - //console.log("'waitFor()' finished in " + (new Date().getTime() - start) + "ms."); - typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled - clearInterval(interval); //< Stop this interval - } - } - }, 100); //< repeat check every 100ms -}; - -if (system.args.length !== 2) { - console.log('Usage: run-qunit.js URL'); - phantom.exit(1); -} - -var fs = require('fs'); -var page = require('webpage').create(); - -// Route "console.log()" calls from within the Page context to the main Phantom context (i.e. current "this") -page.onConsoleMessage = function(msg) { - console.log(msg); -}; -page.onError = function (msg, trace) { - console.log(msg); - trace.forEach(function(item) { - console.log(' ', item.file, ':', item.line); - }) -} - -var _openPath = phantom.args[0].replace(/^.*(\\|\/)/, ''); -var openPath = _openPath; -var origdir = '../js/'; -var basedir = '../instrumented/'; -var coverageBase = fs.read('_coverage.html'); - -if (fs.exists(basedir)){ - var script = /<\/script>/g, - src = /src=(["'])(.*?)\1/, - contents = fs.read(openPath), - _contents = contents, - srcs = [], - s; - while (script.exec(contents)){ - s = src.exec(RegExp.lastMatch)[2]; - if (s && s.indexOf(origdir) != -1) - _contents = _contents.replace(s, s.replace(origdir, basedir)) - } - if (_contents != contents){ - openPath += '.cov.html'; - fs.write(openPath, _contents); - } -} - -page.open(openPath, function(status){ - if (status !== "success") { - console.log("Unable to access network"); - phantom.exit(1); - } else { - // Inject instrumented sources if they exist - if (fs.exists(basedir)) - for (var i=0; i0 ? 'hit' : 'miss'); - } else { - hitmiss = ' ' + 'undef'; - } - var htmlLine = fileLines[idx] - if (!source) - htmlLine = htmlLine.replace('<', '<').replace('>', '>'); - colorized += '
' + htmlLine + '
\n'; - }; - colorized = coverageBase.replace('COLORIZED_LINE_HTML', colorized); - - fs.write('coverage.html', colorized, 'w'); - - console.log('Coverage for ' + coverageInfo.key + ' in coverage.html'); - } - if (_openPath != openPath) - fs.remove(openPath); - - var failedNum = page.evaluate(function(){ - var el = document.getElementById('qunit-testresult'); - console.log(el.innerText); - try { - return el.getElementsByClassName('failed')[0].innerHTML; - } catch (e) { } - return 10000; - }); - phantom.exit((parseInt(failedNum, 10) > 0) ? 1 : 0); - }); - } -}); - -function readFileLines(filename) { - var stream = fs.open(filename, 'r'); - var lines = []; - var line; - while (!stream.atEnd()) { - lines.push(stream.readLine()); - } - stream.close(); - - return lines; -} - diff --git a/tests/tests.html b/tests/tests.html index 6d069cc7a..d7a6e7756 100644 --- a/tests/tests.html +++ b/tests/tests.html @@ -4,7 +4,6 @@ -