grunt-contrib-jasmine/tasks/jasmine.js

225 lines
6.7 KiB
JavaScript
Raw Normal View History

2012-11-02 13:03:09 +08:00
/*
* grunt-contrib-jasmine
* http://gruntjs.com/
*
* Licensed under the MIT license.
*/
/*jshint node:true, curly:false*/
2012-11-02 13:03:09 +08:00
'use strict';
module.exports = function(grunt) {
// node api
var fs = require('fs'),
path = require('path');
// npm lib
var phantomjs = require('grunt-lib-phantomjs').init(grunt);
// local lib
var jasmine = require('./lib/jasmine');
var status = {
specs : 0,
failed : 0,
passed : 0,
total : 0,
skipped : 0,
duration : 0
};
var runners = {
default : __dirname + '/jasmine/templates/DefaultRunner.tmpl',
requirejs : __dirname + '/jasmine/templates/RequireJSRunner.tmpl'
};
var runnerOptions = {
requirejs : {
requirejs : jasmine.getRelativeFileList(__dirname + '/../vendor/require-2.1.1.js')[0]
}
};
2012-11-02 13:03:09 +08:00
grunt.registerMultiTask('jasmine', 'Run jasmine specs headlessly through PhantomJS.', function() {
2012-11-02 13:03:09 +08:00
// Merge task-specific options with these defaults.
var options = this.options({
timeout : 10000,
specs : [],
helpers : [],
vendor : [],
outfile : '_SpecRunner.html',
host : '',
template: 'default',
templateOptions : {},
phantomjs : {}
2012-11-02 13:03:09 +08:00
});
grunt.util._.defaults(options.templateOptions, runnerOptions[options.template] || {});
if (!options.template.match(/\.tmpl$/)) options.template = runners[options.template];
2012-11-02 13:03:09 +08:00
if (grunt.option('debug')) {
grunt.log.debug(options);
2012-11-02 13:03:09 +08:00
}
setup(options);
var files = grunt.file.expandFiles(grunt.util._.pluck(this.files, 'src'));
jasmine.buildSpecrunner(files,options);
// If we're just building (e.g. for web), skip phantom.
if (this.flags.build) return;
var done = this.async();
phantomRunner(options, function(err,status) {
if (err) grunt.log.error(err);
if (status.failed === 0) grunt.log.ok('0 failures');
else grunt.log.error(status.failed + ' failures');
teardown(options);
done(!err && status.failed === 0);
});
2012-11-02 13:03:09 +08:00
});
function phantomRunner(options,cb){
var file = options.outfile;
if (options.host) {
file = options.host + options.outfile;
}
grunt.verbose.subhead('Testing jasmine specs via phantom').or.writeln('Testing jasmine specs via phantom');
phantomjs.spawn(file, {
failCode : 90,
options : options,
done : function(err){
cb(err,status);
}
});
}
function teardown(options) {
if (fs.statSync(options.outfile).isFile()) fs.unlink(options.outfile);
// Have to explicitly unregister nested wildcards. Need to file a bug for EventEmitter2
phantomjs.removeAllListeners('*');
phantomjs.removeAllListeners('jasmine.*');
phantomjs.removeAllListeners('error.*');
phantomjs.removeAllListeners('jasmine.done.*');
}
function setup(options) {
var thisRun = {};
phantomjs.on('fail.timeout',function(){
grunt.log.writeln();
grunt.warn('PhantomJS timed out, possibly due to an unfinished async spec.', 90);
});
phantomjs.on('console',console.log.bind(console));
phantomjs.on('verbose',grunt.verbose.writeln.bind(grunt.verbose));
phantomjs.on('debug', grunt.log.debug.bind(grunt.log, 'phantomjs'));
phantomjs.on('write', grunt.log.write.bind(grunt.log));
phantomjs.on('writeln', grunt.log.writeln.bind(grunt.log));
phantomjs.on('error.onError',function(string, trace){
if (trace) {
grunt.log.error(string.red + ' at ');
trace.forEach(function(line) {
var file = line.file.replace(/^file:/,'');
var message = grunt.util._('%s:%d %s').sprintf(path.relative('.',file), line.line, line.function);
grunt.log.error(message.red);
});
} else {
grunt.log.error(string.red);
}
});
phantomjs.onAny(function() {
var args = [this.event].concat(grunt.util.toArray(arguments));
grunt.event.emit.apply(grunt.event, args);
});
phantomjs.on('jasmine.writeFile',function(type,filename, xml){
var dir = options[type] && options[type].output;
if (dir) {
grunt.file.mkdir(dir);
grunt.file.write(path.join(dir, filename), xml);
}
});
phantomjs.on('jasmine.reportRunnerStarting',function(suites) {
grunt.verbose.writeln('Starting...');
thisRun.start_time = (new Date()).getTime();
thisRun.executed_specs = 0;
thisRun.passed_specs = 0;
});
phantomjs.on('jasmine.reportSpecStarting',function(spec) {
thisRun.executed_specs++;
grunt.verbose.write(spec.suite.description + ':' + spec.description + '...');
});
phantomjs.on('jasmine.reportSuiteResults',function(suite){
//grunt.verbose.writeln(suite.description + ": " + suite.results.passedCount + " of " + suite.results.totalCount + " passed.");
});
phantomjs.on('jasmine.reportSpecResults',function(specId, result,summary) {
if (result.passed) thisRun.passed_specs++;
grunt.verbose.writeln(summary.passed ? result.msg.green : result.msg.red);
for (var i = 0; i < result.messages.length; i++) {
var item = result.messages[i];
if (item.type === 'log') {
grunt.verbose.writeln(item.toString());
} else if (item.type === 'expect' && !item.passed_) {
grunt.log.writeln(summary.description + ':' + result.msg.red);
phantomjs.emit('onError', item.message, item.trace);
}
}
phantomjs.emit( 'jasmine.testDone', result.totalCount, result.passedCount, result.failedCount, result.skipped );
});
phantomjs.on('jasmine.reportRunnerResults',function(){
grunt.verbose.writeln('Runner finished');
var dur = (new Date()).getTime() - thisRun.start_time;
var failed = thisRun.executed_specs - thisRun.passed_specs;
var spec_str = thisRun.executed_specs + (thisRun.executed_specs === 1 ? " spec, " : " specs, ");
var fail_str = failed + (failed === 1 ? " failure in " : " failures in ");
grunt.log.writeln(spec_str + fail_str + (dur/1000) + "s.");
});
phantomjs.on('jasmine.testDone',function(totalAssertions, passedAssertions, failedAssertions, skippedAssertions){
status.specs++;
status.failed += failedAssertions;
status.passed += passedAssertions;
status.total += totalAssertions;
status.skipped += skippedAssertions;
});
phantomjs.on('jasmine.done',function(elapsed){
phantomjs.halt();
status.duration = elapsed;
});
phantomjs.on('jasmine.done.PhantomReporter',function(){
phantomjs.emit('jasmine.done');
});
phantomjs.on('jasmine.done_fail',function(url){
grunt.log.error();
grunt.warn('PhantomJS unable to load "' + url + '" URI.', 90);
});
}
2012-11-02 13:03:09 +08:00
};