@@ -5,9 +5,12 @@ var path = require('path');
55var gulp = require ( 'gulp' ) ,
66 through = require ( 'through2' ) ,
77 querystring = require ( 'querystring' ) ,
8+ lodashMerge = require ( 'lodash.merge' ) ,
89 childProcess = require ( 'child_process' ) ;
910
10- var yargs = require ( '../util/yargs' ) ;
11+ var yargs = require ( '../util/yargs' ) ,
12+ streams = require ( '../config/streams' ) ,
13+ platform = require ( '../config/platform' ) ;
1114
1215/**
1316 * Wrap the given value in quotation marks
@@ -18,7 +21,7 @@ function quote(value) {
1821 return '"' + value + '"' ;
1922}
2023
21- var defaultReporterName = 'angularity- karma-reporter' ;
24+ var defaultReporterName = 'karma-angularity -reporter' ;
2225
2326var filesAppendRegex = / \/ \* + \s * A N G U L A R I T Y _ F I L E _ L I S T \s * \* + \/ / ;
2427var reportersAppendRegex = / \/ \* + \s * A N G U L A R I T Y _ R E P O R T E R _ L I S T \s * \* + \/ / ;
@@ -27,6 +30,10 @@ var karmaReporterMatchNameRegex = /^karma-(.+)-reporter$/ ;
2730
2831/**
2932 * Determine the `require` path for a plugin.
33+ * Note that this require path must be absolute,
34+ * as it will not be required within this project,
35+ * but rather by the karma runner - and therefore the current directory
36+ * cannot be predicted.
3037 *
3138 * @param {string } reporterName Either a package name,
3239 * *OR* an absolute path, for a karma reporter.
@@ -38,29 +45,34 @@ function getKarmaReporterPluginPath(reporterName) {
3845 throw 'Get Karma Reporter Plugin Path: Reporter name is unspecified' ;
3946 }
4047
41- if ( reporterName === defaultReporterName ) {
42- return defaultReporterName ;
48+ //Default reporter is a dependency of angularity
49+ var reporterPath = ( reporterName === defaultReporterName ) ?
50+ path . resolve ( __dirname , '../../node_modules' , defaultReporterName ) :
51+ reporterName ;
52+ //Specific reporters are expected to be dependencies of the local project
53+ reporterPath = ( path . dirname ( reporterPath ) === '.' ) ?
54+ path . resolve ( 'node_modules' , 'karma-' + reporterName + '-reporter' ) :
55+ reporterPath ;
56+
57+ //Either way, the absolute path of the reporter is `require`d
58+ //Only to determine if it is indeed `require`able,
59+ //and enable fail fast if it is not.
60+ try {
61+ require ( reporterPath ) ;
4362 }
44- else {
45- var reporterPath = ( path . dirname ( reporterName ) === '.' ) ?
46- path . resolve ( 'node_modules' , 'karma-' + reporterName + '-reporter' ) :
47- reporterName ;
48- try {
49- require ( reporterPath ) ;
50- }
51- catch ( ex ) {
52- throw 'Get Karma Reporter Plugin Path: Attempt to require reporter from path ' +
53- reporterPath + ' with no success.' ;
54- }
55- reporterPath = path . normalize ( reporterPath ) ;
56- //quirk: nodejs identifies all windows (both win32 and win64) as win32
57- if ( process . platform === 'win32' ) {
58- //replace any single backslash characters in file paths with
59- //double backslash on windows; neither path.normalize nor path.resolve do this
60- reporterPath = reporterPath . replace ( / \\ / g , '\\\\' ) ;
61- }
62- return reporterPath ;
63+ catch ( ex ) {
64+ throw 'Get Karma Reporter Plugin Path: Attempt to require reporter from path ' +
65+ reporterPath + ' with no success.' ;
6366 }
67+ reporterPath = path . normalize ( reporterPath ) ;
68+
69+ if ( platform . isWindows ( ) ) {
70+ //Sanitise path for Windows
71+ //Replace any single backslash characters in file paths with
72+ //double backslash on windows; neither path.normalize nor path.resolve do this
73+ reporterPath = reporterPath . replace ( / \\ / g , '\\\\' ) ;
74+ }
75+ return reporterPath ;
6476}
6577
6678/**
@@ -106,37 +118,70 @@ function karmaCreateConfig(reporters, configFileName) {
106118 configFileName = configFileName || 'karma.conf.js' ;
107119 var files = [ ] ;
108120
109- function transformFn ( file , encoding , done ) {
121+ function transformFn ( file , encoding , transformDone ) {
110122 if ( ! file || ! file . path ) {
111123 throw 'Files must have paths' ;
112124 }
113- files . push ( file . path ) ;
114- done ( ) ;
125+ if ( path . extname ( file . path ) === '.js' ) {
126+ files . push ( file . path ) ;
127+ }
128+ else {
129+ //non-javascript files, such as source maps, should be included but not tested
130+ files . push ( {
131+ pattern : file . path ,
132+ included : false ,
133+ } ) ;
134+ }
135+ transformDone ( ) ;
115136 }
116137
117- function flushFn ( done ) {
138+ function flushFn ( flushDone ) {
118139 var stream = this ;
119- var filesAppend = JSON . stringify ( files , null , ' ' ) ;
120- var reportersAppend = JSON . stringify ( reporters . map ( getKarmaReporterName ) , null , ' ' ) ;
121- var pluginsAppend = '[\n' + reporters . map ( function ( reporter ) {
122- return 'require("' + getKarmaReporterPluginPath ( reporter ) + '")' ;
123- } ) . join ( ',\n ' ) + '\n]' ;
124-
125- //aggregate and append to karma.conf.js in the project root folder
126- gulp
127- . src ( path . resolve ( configFileName ) )
128- . on ( 'data' , function ( karmaConfigFile ) {
129- var contents = karmaConfigFile . contents . toString ( )
130- . replace ( filesAppendRegex , filesAppend )
131- . replace ( reportersAppendRegex , reportersAppend )
132- . replace ( reportersAppendRegex , reportersAppend )
133- . replace ( pluginsAppendRegex , pluginsAppend ) ;
134- karmaConfigFile . contents = new Buffer ( contents ) ;
135- stream . push ( karmaConfigFile ) ;
136- } )
137- . on ( 'end' , function ( ) {
138- done ( ) ;
140+
141+ function constructFile ( ) {
142+ var filesAppend = JSON . stringify ( files , null , ' ' ) ;
143+ var reportersAppend = JSON . stringify ( reporters . map ( getKarmaReporterName ) , null , ' ' ) ;
144+ var pluginsAppend = '[\n' + reporters . map ( function ( reporter ) {
145+ return 'require("' + getKarmaReporterPluginPath ( reporter ) + '")' ;
146+ } ) . join ( ',\n ' ) + '\n]' ;
147+
148+ //aggregate and append to karma.conf.js in the project root folder
149+ gulp
150+ . src ( path . resolve ( configFileName ) )
151+ . on ( 'data' , function ( karmaConfigFile ) {
152+ var contents = karmaConfigFile . contents . toString ( )
153+ . replace ( filesAppendRegex , filesAppend )
154+ . replace ( reportersAppendRegex , reportersAppend )
155+ . replace ( reportersAppendRegex , reportersAppend )
156+ . replace ( pluginsAppendRegex , pluginsAppend ) ;
157+ karmaConfigFile . contents = new Buffer ( contents ) ;
158+ stream . push ( karmaConfigFile ) ;
159+ } )
160+ . on ( 'end' , function ( ) {
161+ flushDone ( ) ;
162+ } ) ;
163+ }
164+
165+ function specTransformFn ( file , encoding , specFileDone ) {
166+ files . push ( {
167+ pattern : file . path ,
168+ included : false ,
139169 } ) ;
170+ specFileDone ( ) ;
171+ }
172+
173+ function specFlushFn ( specFlushDone ) {
174+ //delay construction of file until all *.spec.js files have been
175+ //added to the files array
176+ constructFile ( ) ;
177+ specFlushDone ( ) ;
178+ }
179+
180+ //obtain a list of *.spec.js files (file contents skipped)
181+ //and add them to the list of files, with `included: false`,
182+ //prior to writing to file
183+ streams . jsSpec ( { read : false } )
184+ . pipe ( through . obj ( specTransformFn , specFlushFn ) ) ;
140185 }
141186
142187 return through . obj ( transformFn , flushFn ) ;
@@ -149,13 +194,15 @@ function karmaCreateConfig(reporters, configFileName) {
149194 *
150195 * @param {Array.<string> } [reporters] The name of the karma reporter to use
151196 * @param {number } [bannerWidth] The width of banner comment, zero or omitted for none
152- * @returns {stream.Through } A through strconcateam that performs the operation of a gulp stream
197+ * @returns {stream.Through } A through stream intended to have a gulp stream piped through it
153198 */
154199function karmaRun ( reporters , bannerWidth ) {
155200 var options = {
156201 configFile : undefined
157202 } ;
158- reporters = reporters || [ ] ;
203+ if ( reporters . constructor !== Array || reporters . length === 0 ) {
204+ throw new Error ( 'No reporters specified' ) ;
205+ }
159206
160207 return through . obj ( function transformFn ( file , encoding , done ) {
161208 options . configFile = file . path ;
@@ -164,45 +211,20 @@ function karmaRun(reporters, bannerWidth) {
164211 function streamFn ( done ) {
165212 var appPath = path . join ( __dirname , 'karma-background.js' ) ;
166213 var data = querystring . escape ( JSON . stringify ( options ) ) ;
167- var command = [ 'node' , quote ( appPath ) , data ] . join ( ' ' ) ;
168-
169- childProcess . exec ( command , { cwd : process . cwd ( ) } , function reporter ( stderr , stdout ) {
170- if ( reporters . length > 0 ) {
171- if ( stdout ) {
172- process . stdout . write ( stdout ) ;
173- }
174- if ( stderr ) {
175- process . stderr . write ( stderr ) ;
176- }
177- done ( ) ;
178- return ;
179- }
180214
181- //TODO @bguiz replace reporter function with a standard karma reporter,
182- //and extract it to own module
183-
184- //default reporter by parsing the stdout and stderr of karma
185- var report ;
186- if ( stdout ) {
187- report = stdout
188- . replace ( / ^ \s + / gm, '' ) // remove leading whitespace
189- . replace ( / ^ ( L O G .* \n $ ) / gm, options . silent ? '' : '$1' ) // remove logging
190- . replace ( / a t \s + n u l l \. .* / gm, '' ) // remove file reference
191- . replace ( / \n \n / gm, '\n' ) // consolidate consecutive line breaks
192- . replace ( / ^ \n | \n $ / g, '' ) ; // remove leading and trailing line breaks overall
193- }
194- else if ( stderr ) {
195- var analysis = / $ E r r o r \: \s * ( .* ) $ / mg. exec ( stderr ) ;
196- report = analysis ? analysis [ 1 ] : stderr ;
197- }
198- if ( report ) {
199- var width = Number ( bannerWidth ) || 0 ;
200- var hr = new Array ( width + 1 ) ; // this is a good trick to repeat a character N times
201- var start = ( width > 0 ) ? ( hr . join ( '\u25BC' ) + '\n' ) : '' ;
202- var stop = ( width > 0 ) ? ( hr . join ( '\u25B2' ) + '\n' ) : '' ;
203- process . stdout . write ( start + '\n' + report + '\n\n' + stop ) ;
204- }
215+ var karmaBackground = childProcess . spawn ( 'node' , [ appPath , data ] , {
216+ cwd : process . cwd ( ) ,
217+ stdio : [ process . stdin , process . stdout , process . stderr ] ,
218+ env : lodashMerge ( process . env , {
219+ //NOTE this workaround is necessary, see issue:
220+ //https://github.com/sindresorhus/supports-color/issues/13
221+ //TODO @bguiz remove workaround when issue has been resolved
222+ SUPPORTS_COLOR : true ,
223+ } ) ,
224+ } ) ;
225+ karmaBackground . on ( 'close' , function ( exitCode ) {
205226 done ( ) ;
227+ return ;
206228 } ) ;
207229 } ) ;
208230} ;
0 commit comments