Skip to content

Commit 57bbffa

Browse files
committed
Merge pull request #17 from bguiz/karma-reporter-dynamic
Karma reporter dynamic
2 parents efa3970 + 3b77abe commit 57bbffa

File tree

4 files changed

+165
-29
lines changed

4 files changed

+165
-29
lines changed

lib/test/karma.js

Lines changed: 133 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ var gulp = require('gulp'),
77
querystring = require('querystring'),
88
childProcess = require('child_process');
99

10+
var yargs = require('../util/yargs');
11+
1012
/**
1113
* Wrap the given value in quotation marks
1214
* @param {*} value The value to wrap
@@ -16,7 +18,73 @@ function quote(value) {
1618
return '"' + value + '"';
1719
}
1820

21+
var defaultReporterName = 'angularity-karma-reporter';
22+
1923
var filesAppendRegex = /\/\*+\s*ANGULARITY_FILE_LIST\s*\*+\// ;
24+
var reportersAppendRegex = /\/\*+\s*ANGULARITY_REPORTER_LIST\s*\*+\// ;
25+
var pluginsAppendRegex = /\/\*+\s*ANGULARITY_PLUGIN_LIST\s*\*+\// ;
26+
var karmaReporterMatchNameRegex = /^karma-(.+)-reporter$/ ;
27+
28+
/**
29+
* Determine the `require` path for a plugin.
30+
*
31+
* @param {string} reporterName Either a package name,
32+
* *OR* an absolute path, for a karma reporter.
33+
* @return {string} In karma config file, `plugins` will be inline `require`d
34+
* using this return value.
35+
*/
36+
function getKarmaReporterPluginPath(reporterName) {
37+
if (typeof reporterName !== 'string') {
38+
throw 'Get Karma Reporter Plugin Path: Reporter name is unspecified';
39+
}
40+
41+
if (reporterName === defaultReporterName) {
42+
return defaultReporterName;
43+
}
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+
}
64+
}
65+
66+
/**
67+
* Determine the registered reporter name.
68+
*
69+
* @param {string} reporterName Either the registered reporter name,
70+
* *OR* an absolute path, for a karma reporter.
71+
* @return {string} The registered reporter name for use in
72+
* the `reporters` section of the karma config file.
73+
*/
74+
function getKarmaReporterName(reporterName) {
75+
if (typeof reporterName !== 'string') {
76+
throw 'Get Karma Reporter Name: Reporter name is unspecified';
77+
}
78+
79+
var name = (path.dirname(reporterName) === '.') ?
80+
reporterName :
81+
path.basename(reporterName);
82+
var match = karmaReporterMatchNameRegex.exec(name);
83+
if (!!match && match.length === 2) {
84+
name = match[1];
85+
}
86+
return name
87+
}
2088

2189
/**
2290
* Create a through2 object stream.
@@ -27,13 +95,15 @@ var filesAppendRegex = /\/\*+\s*ANGULARITY_FILE_LIST\s*\*+\// ;
2795
* The new karma config file is added to the stream,
2896
* All input `*.js` files are filtered out of the stream
2997
*
30-
* @param {string} configFileName The project local karma config file to augment
98+
* @param {Array.<string>} [reporters] The name of the karma reporter to use
99+
* @param {string} [configFileName] The project local karma config file to augment
31100
* Defaults to "karma.conf.js"
32101
* @return {stream.Through} The output of this stream is expected to contain
33102
* just one file: the augmented karma config file.
34103
*/
35-
function karmaCreateConfig(configFileName) {
36-
configFileName = 'karma.conf.js';
104+
function karmaCreateConfig(reporters, configFileName) {
105+
reporters = reporters || [];
106+
configFileName = configFileName || 'karma.conf.js';
37107
var files = [];
38108

39109
function transformFn(file, encoding, done) {
@@ -47,13 +117,20 @@ function karmaCreateConfig(configFileName) {
47117
function flushFn(done) {
48118
var stream = this;
49119
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]';
50124

51125
//aggregate and append to karma.conf.js in the project root folder
52126
gulp
53127
.src(path.resolve(configFileName))
54128
.on('data', function(karmaConfigFile) {
55-
var contents = karmaConfigFile.contents.toString();
56-
contents = contents.replace(filesAppendRegex, filesAppend);
129+
var contents = karmaConfigFile.contents.toString()
130+
.replace(filesAppendRegex, filesAppend)
131+
.replace(reportersAppendRegex, reportersAppend)
132+
.replace(reportersAppendRegex, reportersAppend)
133+
.replace(pluginsAppendRegex, pluginsAppend);
57134
karmaConfigFile.contents = new Buffer(contents);
58135
stream.push(karmaConfigFile);
59136
})
@@ -70,13 +147,15 @@ function karmaCreateConfig(configFileName) {
70147
* No output. Ends when the Karma process ends.
71148
* Runs karma in a child process, to avoid `process.exit()` called by karma.
72149
*
150+
* @param {Array.<string>} [reporters] The name of the karma reporter to use
73151
* @param {number} [bannerWidth] The width of banner comment, zero or omitted for none
74152
* @returns {stream.Through} A through strconcateam that performs the operation of a gulp stream
75153
*/
76-
function karmaRun(bannerWidth) {
154+
function karmaRun(reporters, bannerWidth) {
77155
var options = {
78156
configFile: undefined
79157
};
158+
reporters = reporters || [];
80159

81160
return through.obj(function transformFn(file, encoding, done) {
82161
options.configFile = file.path;
@@ -87,23 +166,32 @@ function karmaRun(bannerWidth) {
87166
var data = querystring.escape(JSON.stringify(options));
88167
var command = [ 'node', quote(appPath), data ].join(' ');
89168

90-
//TODO @bguiz replace reporter function with a standard karma reporter,
91-
//and extract it to own module
92-
//perhaps extend the spec reporter to do what is being done here, instead of post processing its output here
93-
//check if there is a webstorm/ teamcity reporter
94169
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+
}
180+
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
95185
var report;
96186
if (stdout) {
97-
console.log(stdout);
98187
report = stdout
99188
.replace(/^\s+/gm, '') // remove leading whitespace
100189
.replace(/^(LOG.*\n$)/gm, options.silent ? '' : '$1') // remove logging
101190
.replace(/at\s+null\..*/gm, '') // remove file reference
102191
.replace(/\n\n/gm, '\n') // consolidate consecutive line breaks
103192
.replace(/^\n|\n$/g, ''); // remove leading and trailing line breaks overall
104-
} else if (stderr) {
105-
console.log(stderr);
106-
193+
}
194+
else if (stderr) {
107195
var analysis = /$Error\:\s*(.*)$/mg.exec(stderr);
108196
report = analysis ? analysis[1] : stderr;
109197
}
@@ -119,7 +207,37 @@ function karmaRun(bannerWidth) {
119207
});
120208
};
121209

210+
var yargsOptionDefiniton = {
211+
key: 'karmareporter',
212+
value: {
213+
describe: 'Specify a custom Karma reporter to use. ' +
214+
'Either a locally npm installed module, or an asolute path to one.',
215+
alias: ['k'],
216+
default: defaultReporterName,
217+
string:true,
218+
}
219+
};
220+
var checkKarmaReporter = yargs.createCheck()
221+
.withGate(function (argv) {
222+
return !argv.help;
223+
})
224+
.withTest({
225+
karmareporter: function(value) {
226+
if (typeof value !== 'undefined') {
227+
try {
228+
getKarmaReporterPluginPath(value);
229+
}
230+
catch (ex) {
231+
return 'Illegal value for "reporter"\n' + ex;
232+
}
233+
}
234+
},
235+
})
236+
.commit();
237+
122238
module.exports = {
123239
createConfig: karmaCreateConfig,
124-
run: karmaRun
240+
run: karmaRun,
241+
yargsCheck: checkKarmaReporter,
242+
yargsOption: yargsOptionDefiniton,
125243
};

lib/util/jshint-reporter.js

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,6 @@ var gulpJshint = require('gulp-jshint');
77
var yargs = require('./yargs');
88

99
var defaultReporterName = 'angularity-jshint-reporter';
10-
var yargsOptionDefiniton = {
11-
key: 'reporter',
12-
value: {
13-
describe: 'Specify a custom JsHint reporter to use. '+
14-
'Either a locally npm installed module, or an asolute path to one.',
15-
alias: ['r'],
16-
default: defaultReporterName,
17-
string:true,
18-
}
19-
};
2010

2111
/**
2212
* Dynamically load a JsHint reporter
@@ -35,7 +25,7 @@ var yargsOptionDefiniton = {
3525
var jsHintReporter;
3626
function getJsHintReporter(reporterName) {
3727
if (typeof reporterName !== 'string') {
38-
throw 'Reporter name is unspecified';
28+
throw 'Get JsHint Reporter: Reporter name is unspecified';
3929
}
4030
if (jsHintReporter) {
4131
return jsHintReporter; //cached copy
@@ -64,6 +54,16 @@ function getJsHintReporter(reporterName) {
6454
return jsHintReporter;
6555
}
6656

57+
var yargsOptionDefiniton = {
58+
key: 'reporter',
59+
value: {
60+
describe: 'Specify a custom JsHint reporter to use. '+
61+
'Either a locally npm installed module, or an asolute path to one.',
62+
alias: ['r'],
63+
default: defaultReporterName,
64+
string: true,
65+
}
66+
};
6767
var checkJsHintReporter = yargs.createCheck()
6868
.withGate(function (argv) {
6969
return !argv.help;

tasks/javascript.js

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,11 @@ yargs.getInstance('test')
6464
boolean : true
6565
})
6666
.options(jshintReporter.yargsOption.key, jshintReporter.yargsOption.value)
67+
.options(karma.yargsOption.key, karma.yargsOption.value)
6768
.strict()
6869
.check(yargs.subCommandCheck)
6970
.check(jshintReporter.yargsCheck)
71+
.check(karma.yargsCheck)
7072
.wrap(80);
7173

7274
gulp.task('javascript', function (done) {
@@ -113,6 +115,17 @@ gulp.task('javascript:lint', function () {
113115

114116
// karma unit tests in local library only
115117
gulp.task('javascript:unit', function () {
118+
var reporters = cliArgs.karmareporter;
119+
if (reporters.constructor === Array) {
120+
reporters = Array;
121+
}
122+
else if (typeof reporters === 'string' &&
123+
reporters !== karma.yargsOption.value.default) {
124+
reporters = [reporters];
125+
}
126+
else {
127+
reporters = [];
128+
}
116129
return combined.create()
117130
.append(
118131
streams
@@ -130,9 +143,9 @@ gulp.task('javascript:unit', function () {
130143
.pipe(gulp.dest(streams.TEST))
131144
.pipe(gulpFilter('*.js'))
132145
)
133-
.pipe(karma.createConfig())
146+
.pipe(karma.createConfig(reporters))
134147
.pipe(gulp.dest(streams.TEST))
135-
.pipe(karma.run(80));
148+
.pipe(karma.run(reporters, 80));
136149
});
137150

138151
// give a single optimised javascript file in the build directory with source map for each

templates/angularity/karma.conf.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,14 @@ module.exports = function(config) {
1919
// list of files to exclude
2020
exclude: [],
2121

22+
// register any plugins which are not siblings of karma in angularity global
23+
// installation and thus need to be registered manually
24+
// append to existing value to preserve plugins loaded automatically
25+
plugins: [].concat(config.plugins, /*ANGULARITY_PLUGIN_LIST*/),
26+
2227
// use dots reporter, as travis terminal does not support escaping sequences
2328
// possible values: 'dots', 'progress', 'junit', 'teamcity'
24-
reporter: [],
29+
reporters: [].concat(/*ANGULARITY_REPORTER_LIST*/),
2530

2631
// web server port
2732
port: <%= port %>,

0 commit comments

Comments
 (0)