2013-12-03 15:33:34 -05:00
#!/usr/bin/env node
var ansi = require ( 'ansi' ) ;
var program = require ( 'commander' ) ;
var path = require ( 'path' ) ;
2013-12-17 18:00:45 -05:00
var fs = require ( 'fs' ) ;
2013-12-03 15:33:34 -05:00
var make _list = function ( val ) {
return val . split ( ',' ) ;
2013-12-17 17:56:18 -05:00
} ;
2013-12-03 15:33:34 -05:00
program
. option ( '-t, --tests <testlist>' , 'Run the specified html-file-based test(s). \'testlist\' should be a comma-separated list' , make _list , [ ] )
. option ( '-a, --print-all' , 'Print all tests, not just the failures' )
. option ( '--disable-color' , 'Explicitly disable color' )
. option ( '-c, --color' , 'Explicitly enable color (default is to use color when not outputting to a pipe)' )
. option ( '-i, --auto-inject <includefiles>' , 'Treat the test list as a set of mocha JS files, and automatically generate HTML files with which to test test. \'includefiles\' should be a comma-separated list of paths to javascript files to include in each of the generated HTML files' , make _list , null )
. option ( '-p, --provider <name>' , 'Use the given provider (defaults to "casper"). Currently, may be "casper" or "zombie"' , 'casper' )
2014-06-03 11:02:27 -04:00
. option ( '-g, --generate-html' , 'Instead of running the tests, just return the path to the generated HTML file, then wait for user interaction to exit (should be used with .js tests).' )
. option ( '-o, --open-in-browser' , 'Open the generated HTML files in a web browser using the "open" module (must be used with the "-g"/"--generate-html" option).' )
2014-09-22 21:42:32 -04:00
. option ( '--output-html' , 'Instead of running the tests, just output the generated HTML source to STDOUT (should be used with .js tests)' )
2013-12-17 17:56:18 -05:00
. option ( '-d, --debug' , 'Show debug output (the "console" event) from the provider' )
2014-09-22 21:42:32 -04:00
. option ( '-r, --relative' , 'Use relative paths in the generated HTML file' )
2015-05-28 15:07:43 -04:00
. option ( '--debugger <port>' , 'Enable the remote debugger for CasperJS' )
2013-12-03 15:33:34 -05:00
. parse ( process . argv ) ;
2013-12-17 18:00:45 -05:00
if ( program . tests . length === 0 ) {
program . tests = fs . readdirSync ( _ _dirname ) . filter ( function ( f ) { return ( /^test\.(\w|\.|-)+\.js$/ ) . test ( f ) ; } ) ;
2014-09-15 16:44:36 -04:00
program . tests = program . tests . map ( function ( f ) { return path . resolve ( _ _dirname , f ) ; } ) ; // add full paths in
2013-12-17 18:00:45 -05:00
console . log ( 'using files %s' , program . tests ) ;
}
2013-12-03 15:33:34 -05:00
var file _paths = [ ] ;
2013-12-17 18:00:57 -05:00
var all _js = program . tests . reduce ( function ( a , e ) { return a && e . slice ( - 3 ) == '.js' ; } , true ) ;
2014-09-22 21:42:32 -04:00
var get _path = function ( /* arguments */ ) {
if ( program . relative ) {
return path . join . apply ( null , arguments ) ;
} else {
var args = Array . prototype . slice . call ( arguments ) ;
args . unshift ( _ _dirname , '..' ) ;
return path . resolve . apply ( null , args ) ;
}
} ;
var get _path _cwd = function ( /* arguments */ ) {
if ( program . relative ) {
var part _path = path . join . apply ( null , arguments ) ;
return path . relative ( path . join ( _ _dirname , '..' ) , path . resolve ( process . cwd ( ) , part _path ) ) ;
} else {
var args = Array . prototype . slice . call ( arguments ) ;
args . unshift ( process . cwd ( ) ) ;
return path . resolve . apply ( null , args ) ;
}
} ;
2013-12-17 18:00:57 -05:00
if ( all _js && ! program . autoInject ) {
var all _modules = { } ;
// uses the first instance of the string 'requires local modules: '
program . tests . forEach ( function ( testname ) {
var full _path = path . resolve ( process . cwd ( ) , testname ) ;
var content = fs . readFileSync ( full _path ) . toString ( ) ;
var ind = content . indexOf ( 'requires local modules: ' ) ;
if ( ind > - 1 ) {
ind += 'requires local modules: ' . length ;
var eol = content . indexOf ( '\n' , ind ) ;
var modules = content . slice ( ind , eol ) . split ( /,\s*/ ) ;
modules . forEach ( function ( mod ) {
2016-09-14 13:52:53 -04:00
all _modules [ get _path ( 'core/' , mod ) + '.js' ] = 1 ;
2013-12-17 18:00:57 -05:00
} ) ;
}
2014-06-03 11:14:42 -04:00
2014-06-03 13:15:02 -04:00
var fakes _ind = content . indexOf ( 'requires test modules: ' ) ;
2014-06-03 11:14:42 -04:00
if ( fakes _ind > - 1 ) {
fakes _ind += 'requires test modules: ' . length ;
var fakes _eol = content . indexOf ( '\n' , fakes _ind ) ;
var fakes _modules = content . slice ( fakes _ind , fakes _eol ) . split ( /,\s*/ ) ;
fakes _modules . forEach ( function ( mod ) {
2014-09-22 21:42:32 -04:00
all _modules [ get _path ( 'tests/' , mod ) + '.js' ] = 1 ;
2014-06-03 11:14:42 -04:00
} ) ;
}
2013-12-17 18:00:57 -05:00
} ) ;
program . autoInject = Object . keys ( all _modules ) ;
}
2013-12-03 15:33:34 -05:00
if ( program . autoInject ) {
var temp = require ( 'temp' ) ;
temp . track ( ) ;
var template = {
2014-09-22 21:42:32 -04:00
header : "<html>\n<head>\n<meta charset='utf-8' />\n<link rel='stylesheet' href='" + get _path ( 'node_modules/mocha/mocha.css' ) + "'/>\n</head>\n<body><div id='mocha'></div>" ,
2013-12-17 17:56:18 -05:00
script _tag : function ( p ) { return "<script src='" + p + "'></script>" ; } ,
2016-09-03 13:53:47 -04:00
footer : "<script>\nmocha.checkLeaks();\nmocha.globals(['navigator', 'create', 'ClientUtils', '__utils__', 'requestAnimationFrame', 'WebSocket']);\nmocha.run(function () { window.__mocha_done = true; });\n</script>\n</body>\n</html>"
2013-12-03 15:33:34 -05:00
} ;
2014-09-22 21:42:32 -04:00
template . header += "\n" + template . script _tag ( get _path ( 'node_modules/chai/chai.js' ) ) ;
template . header += "\n" + template . script _tag ( get _path ( 'node_modules/mocha/mocha.js' ) ) ;
template . header += "\n" + template . script _tag ( get _path ( 'node_modules/sinon/pkg/sinon.js' ) ) ;
template . header += "\n" + template . script _tag ( get _path ( 'node_modules/sinon-chai/lib/sinon-chai.js' ) ) ;
template . header += "\n" + template . script _tag ( get _path ( 'node_modules/sinon-chai/lib/sinon-chai.js' ) ) ;
2013-12-03 15:33:34 -05:00
template . header += "\n<script>mocha.setup('bdd');</script>" ;
template . header = program . autoInject . reduce ( function ( acc , sn ) {
2014-09-22 21:42:32 -04:00
return acc + "\n" + template . script _tag ( get _path _cwd ( sn ) ) ;
2013-12-03 15:33:34 -05:00
} , template . header ) ;
file _paths = program . tests . map ( function ( jsn , ind ) {
var templ = template . header ;
templ += "\n" ;
2014-09-22 21:42:32 -04:00
templ += template . script _tag ( get _path _cwd ( jsn ) ) ;
2013-12-03 15:33:34 -05:00
templ += template . footer ;
var tempfile = temp . openSync ( { prefix : 'novnc-zombie-inject-' , suffix : '-file_num-' + ind + '.html' } ) ;
fs . writeSync ( tempfile . fd , templ ) ;
fs . closeSync ( tempfile . fd ) ;
return tempfile . path ;
} ) ;
}
else {
file _paths = program . tests . map ( function ( fn ) {
return path . resolve ( process . cwd ( ) , fn ) ;
} ) ;
}
var use _ansi = false ;
if ( program . color ) use _ansi = true ;
else if ( program . disableColor ) use _ansi = false ;
else if ( process . stdout . isTTY ) use _ansi = true ;
var cursor = ansi ( process . stdout , { enabled : use _ansi } ) ;
2013-12-04 15:39:20 -05:00
if ( program . outputHtml ) {
file _paths . forEach ( function ( path , path _ind ) {
fs . readFile ( path , function ( err , data ) {
if ( err ) {
console . warn ( error . stack ) ;
return ;
}
2013-12-17 17:56:18 -05:00
2014-09-22 21:42:32 -04:00
if ( use _ansi ) {
cursor
. bold ( )
. write ( program . tests [ path _ind ] )
. reset ( )
. write ( "\n" )
. write ( Array ( program . tests [ path _ind ] . length + 1 ) . join ( '=' ) )
. write ( "\n\n" ) ;
}
2013-12-04 15:39:20 -05:00
cursor
. write ( data )
2014-09-22 21:42:32 -04:00
. write ( "\n\n" ) ;
2013-12-04 15:39:20 -05:00
} ) ;
} ) ;
}
if ( program . generateHtml ) {
2014-06-03 11:02:27 -04:00
var open _browser ;
if ( program . openInBrowser ) {
open _browser = require ( 'open' ) ;
}
2013-12-04 15:39:20 -05:00
file _paths . forEach ( function ( path , path _ind ) {
cursor
. bold ( )
. write ( program . tests [ path _ind ] )
. write ( ": " )
. reset ( )
. write ( path )
. write ( "\n" ) ;
2014-06-03 11:02:27 -04:00
if ( program . openInBrowser ) {
open _browser ( path ) ;
}
2013-12-04 15:39:20 -05:00
} ) ;
2013-12-03 15:33:34 -05:00
console . log ( '' ) ;
2013-12-04 15:39:20 -05:00
}
2013-12-03 15:33:34 -05:00
2013-12-04 15:39:20 -05:00
if ( program . generateHtml ) {
process . stdin . resume ( ) ; // pause until C-c
process . on ( 'SIGINT' , function ( ) {
process . stdin . pause ( ) ; // exit
} ) ;
}
2013-12-03 15:33:34 -05:00
2013-12-04 15:39:20 -05:00
if ( ! program . outputHtml && ! program . generateHtml ) {
var failure _count = 0 ;
2013-12-03 15:33:34 -05:00
2013-12-04 15:39:20 -05:00
var prov = require ( path . resolve ( _ _dirname , 'run_from_console.' + program . provider + '.js' ) ) ;
2013-12-03 15:33:34 -05:00
cursor
2013-12-04 15:39:20 -05:00
. write ( "Running tests " )
. bold ( )
. write ( program . tests . join ( ', ' ) )
2013-12-03 15:33:34 -05:00
. reset ( )
2013-12-04 15:39:20 -05:00
. grey ( )
. write ( ' using provider ' + prov . name )
2013-12-03 15:33:34 -05:00
. reset ( )
2013-12-04 15:39:20 -05:00
. write ( "\n" ) ;
//console.log("Running tests %s using provider %s", program.tests.join(', '), prov.name);
2013-12-03 15:33:34 -05:00
2015-05-28 15:07:43 -04:00
var provider = prov . provide _emitter ( file _paths , program . debugger ) ;
2013-12-04 15:39:20 -05:00
provider . on ( 'test_ready' , function ( test _json ) {
console . log ( '' ) ;
2013-12-03 15:33:34 -05:00
2013-12-04 15:39:20 -05:00
filename = program . tests [ test _json . file _ind ] ;
2013-12-03 15:33:34 -05:00
2013-12-04 15:39:20 -05:00
cursor . bold ( ) ;
console . log ( 'Results for %s:' , filename ) ;
console . log ( Array ( 'Results for :' . length + filename . length + 1 ) . join ( '=' ) ) ;
cursor . reset ( ) ;
2013-12-03 15:33:34 -05:00
2013-12-04 15:39:20 -05:00
console . log ( '' ) ;
cursor
2013-12-17 17:56:18 -05:00
. write ( '' + test _json . num _tests + ' tests run, ' )
2013-12-04 15:39:20 -05:00
. green ( )
. write ( '' + test _json . num _passes + ' passed' ) ;
if ( test _json . num _slow > 0 ) {
cursor
. reset ( )
. write ( ' (' ) ;
cursor
. yellow ( )
. write ( '' + test _json . num _slow + ' slow' )
. reset ( )
. write ( ')' ) ;
}
cursor
. reset ( )
. write ( ', ' ) ;
cursor
. red ( )
. write ( '' + test _json . num _fails + ' failed' ) ;
2013-12-17 17:56:18 -05:00
if ( test _json . num _skipped > 0 ) {
cursor
. reset ( )
. write ( ', ' )
. grey ( )
. write ( '' + test _json . num _skipped + ' skipped' ) ;
}
2013-12-04 15:39:20 -05:00
cursor
. reset ( )
2013-12-17 17:56:18 -05:00
. write ( ' -- duration: ' + test _json . duration + "s\n" ) ;
2013-12-04 15:39:20 -05:00
console . log ( '' ) ;
if ( test _json . num _fails > 0 || program . printAll ) {
2015-05-28 15:09:31 -04:00
var extract _error _lines = function ( err ) {
// the split is to avoid a weird thing where in PhantomJS where we get a stack trace too
var err _lines = err . split ( '\n' ) ;
if ( err _lines . length == 1 ) {
return err _lines [ 0 ] ;
} else {
var ind ;
for ( ind = 0 ; ind < err _lines . length ; ind ++ ) {
var at _ind = err _lines [ ind ] . trim ( ) . indexOf ( 'at ' ) ;
if ( at _ind === 0 ) {
break ;
}
}
return err _lines . slice ( 0 , ind ) . join ( '\n' ) ;
}
} ;
2013-12-04 15:39:20 -05:00
var traverse _tree = function ( indentation , node ) {
if ( node . type == 'suite' ) {
if ( ! node . has _subfailures && ! program . printAll ) return ;
2013-12-17 17:56:18 -05:00
if ( indentation === 0 ) {
2013-12-04 15:39:20 -05:00
cursor . bold ( ) ;
console . log ( node . name ) ;
console . log ( Array ( node . name . length + 1 ) . join ( '-' ) ) ;
cursor . reset ( ) ;
}
else {
cursor
. write ( Array ( indentation + 3 ) . join ( '#' ) )
. bold ( )
. write ( ' ' + node . name + ' ' )
. reset ( )
. write ( Array ( indentation + 3 ) . join ( '#' ) )
. write ( "\n" ) ;
}
2013-12-03 15:33:34 -05:00
console . log ( '' ) ;
2013-12-04 15:39:20 -05:00
for ( var i = 0 ; i < node . children . length ; i ++ ) {
traverse _tree ( indentation + 1 , node . children [ i ] ) ;
}
2013-12-03 15:33:34 -05:00
}
2013-12-04 15:39:20 -05:00
else {
if ( ! node . pass ) {
cursor . magenta ( ) ;
console . log ( '- failed: ' + node . text + test _json . replay ) ;
cursor . red ( ) ;
2015-05-28 15:09:31 -04:00
console . log ( ' ' + extract _error _lines ( node . error ) ) ;
2013-12-04 15:39:20 -05:00
cursor . reset ( ) ;
console . log ( '' ) ;
}
else if ( program . printAll ) {
2013-12-17 17:56:18 -05:00
if ( node . skipped ) {
cursor
. grey ( )
. write ( '- skipped: ' + node . text ) ;
}
else {
if ( node . slow ) cursor . yellow ( ) ;
else cursor . green ( ) ;
cursor
. write ( '- pass: ' + node . text )
. grey ( )
. write ( ' (' + node . duration + ') ' ) ;
}
2013-12-04 15:39:20 -05:00
/*if (node.slow) cursor.yellow();
else cursor.green();*/
cursor
//.write(test_json.replay)
. reset ( )
. write ( "\n" ) ;
console . log ( '' ) ;
}
2013-12-03 15:33:34 -05:00
}
2013-12-17 17:56:18 -05:00
} ;
2013-12-03 15:33:34 -05:00
2013-12-04 15:39:20 -05:00
for ( var i = 0 ; i < test _json . suites . length ; i ++ ) {
traverse _tree ( 0 , test _json . suites [ i ] ) ;
}
2013-12-03 15:33:34 -05:00
}
2013-12-17 17:56:18 -05:00
if ( test _json . num _fails === 0 ) {
2013-12-04 15:39:20 -05:00
cursor . fg . green ( ) ;
console . log ( 'all tests passed :-)' ) ;
cursor . reset ( ) ;
}
} ) ;
2013-12-03 15:33:34 -05:00
2013-12-17 17:56:18 -05:00
if ( program . debug ) {
provider . on ( 'console' , function ( line ) {
2014-06-03 16:58:37 -04:00
// log to stderr
console . error ( line ) ;
2013-12-17 17:56:18 -05:00
} ) ;
}
2013-12-03 15:33:34 -05:00
2014-06-03 16:58:37 -04:00
provider . on ( 'error' , function ( line ) {
// log to stderr
console . error ( 'ERROR: ' + line ) ;
} ) ;
2013-12-04 15:39:20 -05:00
/*gprom.finally(function(ph) {
ph.exit();
// exit with a status code that actually gives information
if (program.exitWithFailureCount) process.exit(failure_count);
});*/
}