diff --git a/.gitignore b/.gitignore index 282b4dc1..2b037d24 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,7 @@ node_modules build/ sftp-config.json coverage/ -temp/ +instrumented/ .idea /nbproject/private/ .directory diff --git a/Gruntfile.js b/Gruntfile.js index b9757de5..bedec6b1 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,6 +1,7 @@ /* global module */ module.exports = function(grunt) { + var dateFormat = require('dateformat'); // ---------- grunt.loadNpmTasks("grunt-contrib-compress"); @@ -13,6 +14,7 @@ module.exports = function(grunt) { grunt.loadNpmTasks("grunt-eslint"); grunt.loadNpmTasks("grunt-git-describe"); grunt.loadNpmTasks('grunt-text-replace'); + grunt.loadNpmTasks('grunt-istanbul'); // ---------- var packageJson = grunt.file.readJSON("package.json"), @@ -21,6 +23,7 @@ module.exports = function(grunt) { packageDirName = "openseadragon-bin-" + packageJson.version, packageDir = "build/" + packageDirName + "/", releaseRoot = "../site-build/built-openseadragon/", + coverageDir = 'coverage/' + dateFormat(new Date(), 'yyyymmdd-HHMMss'), sources = [ "src/openseadragon.js", "src/fullscreen.js", @@ -83,7 +86,7 @@ module.exports = function(grunt) { clean: { build: ["build"], package: [packageDir], - coverage: ["coverage"], + coverage: ["instrumented"], release: { src: [releaseRoot], options: { @@ -154,7 +157,8 @@ module.exports = function(grunt) { qunit: { normal: { options: { - urls: [ "http://localhost:8000/test/test.html" ] + urls: [ "http://localhost:8000/test/test.html" ], + timeout: 500000 } }, coverage: { @@ -162,11 +166,13 @@ module.exports = function(grunt) { urls: [ "http://localhost:8000/test/coverage.html" ], coverage: { src: ['src/*.js'], - htmlReport: 'coverage/html/', - instrumentedFiles: 'temp/', + htmlReport: coverageDir + '/html/', + instrumentedFiles: 'instrumented/src/', baseUrl: '.', disposeCollector: true - } + }, + inject: 'test/helpers/phantom-bridge.js', + timeout: 500000 } }, all: { @@ -199,7 +205,38 @@ module.exports = function(grunt) { }, build: {} }, - gitInfo: "unknown" + gitInfo: "unknown", + instrument: { + files: sources, + options: { + lazy: false, + basePath: 'instrumented/' + } + }, + reloadTasks: { + rootPath: "instrumented/src/" + }, + storeCoverage: { + options: { + dir: coverageDir, + 'include-all-sources': true + } + }, + makeReport: { + src: "coverage/**/*.json", + options: { + type: [ "lcov", "html" ], + dir: coverageDir, + print: "detail" + } + } + }); + + grunt.event.on("qunit.coverage", function(coverage) { + var reportPath = coverageDir + "/coverage.json"; + + // Create the coverage file + grunt.file.write(reportPath, JSON.stringify(coverage)); }); // ---------- @@ -287,7 +324,7 @@ module.exports = function(grunt) { // ---------- // Coverage task. // Outputs unit test code coverage report. - grunt.registerTask("coverage", ["clean:coverage", "connect", "qunit:coverage"]); + grunt.registerTask("coverage", ["clean:coverage", "instrument", "connect", "qunit:coverage", "makeReport"]); // ---------- // Package task. diff --git a/package.json b/package.json index dc36ae73..efc0ed5f 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,7 @@ "grunt-contrib-watch": "^1.0.0", "grunt-eslint": "^19.0.0", "grunt-git-describe": "^2.3.2", + "grunt-istanbul": "^0.8.0", "grunt-text-replace": "^0.4.0", "qunitjs": "2.4.1" }, diff --git a/test/coverage.html b/test/coverage.html index 36e2e056..e2abc706 100644 --- a/test/coverage.html +++ b/test/coverage.html @@ -21,40 +21,40 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/helpers/phantom-bridge.js b/test/helpers/phantom-bridge.js new file mode 100644 index 00000000..ed5b84ad --- /dev/null +++ b/test/helpers/phantom-bridge.js @@ -0,0 +1,73 @@ +/* + * grunt-contrib-qunit + * http://gruntjs.com/ + * + * Copyright (c) 2016 "Cowboy" Ben Alman, contributors + * Licensed under the MIT license. + */ + +/*global QUnit:true, alert:true*/ +(function (factory) { + if (typeof define === 'function' && define.amd) { + require(['qunit'], factory); + } else { + factory(QUnit); + } +}(function(QUnit) { + 'use strict'; + + // Don't re-order tests. + QUnit.config.reorder = false; + + // Send messages to the parent PhantomJS process via alert! Good times!! + function sendMessage() { + var args = [].slice.call(arguments); + alert(JSON.stringify(args)); + } + + // These methods connect QUnit to PhantomJS. + QUnit.log(function(obj) { + // What is this I don’t even + if (obj.message === '[object Object], undefined:undefined') { return; } + + // Parse some stuff before sending it. + var actual, expected; + if (!obj.result) { + // Dumping large objects can be very slow, and the dump isn't used for + // passing tests, so only dump if the test failed. + actual = QUnit.dump.parse(obj.actual); + expected = QUnit.dump.parse(obj.expected); + } + // Send it. + sendMessage('qunit.log', obj.result, actual, expected, obj.message, obj.source, obj.todo); + }); + + QUnit.testStart(function(obj) { + sendMessage('qunit.testStart', obj.name); + }); + + QUnit.testDone(function(obj) { + sendMessage('qunit.testDone', obj.name, obj.failed, obj.passed, obj.total, obj.runtime, obj.skipped, obj.todo); + }); + + QUnit.moduleStart(function(obj) { + sendMessage('qunit.moduleStart', obj.name); + }); + + QUnit.moduleDone(function(obj) { + sendMessage('qunit.moduleDone', obj.name, obj.failed, obj.passed, obj.total); + }); + + QUnit.begin(function() { + sendMessage('qunit.begin'); + }); + + QUnit.done(function(obj) { + // send coverage data if available + if ( window.__coverage__ ) { + sendMessage( "qunit.coverage", window.__coverage__ ); + } + + sendMessage('qunit.done', obj.failed, obj.passed, obj.total, obj.runtime); + }); +}));