]>
Commit | Line | Data |
---|---|---|
2af86592 SR |
1 | #!/usr/bin/env node |
2 | var ansi = require('ansi'); | |
3 | var program = require('commander'); | |
4 | var path = require('path'); | |
d823e895 | 5 | var fs = require('fs'); |
2af86592 SR |
6 | |
7 | var make_list = function(val) { | |
8 | return val.split(','); | |
8eb88937 | 9 | }; |
2af86592 SR |
10 | |
11 | program | |
12 | .option('-t, --tests <testlist>', 'Run the specified html-file-based test(s). \'testlist\' should be a comma-separated list', make_list, []) | |
13 | .option('-a, --print-all', 'Print all tests, not just the failures') | |
14 | .option('--disable-color', 'Explicitly disable color') | |
15 | .option('-c, --color', 'Explicitly enable color (default is to use color when not outputting to a pipe)') | |
16 | .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) | |
17 | .option('-p, --provider <name>', 'Use the given provider (defaults to "casper"). Currently, may be "casper" or "zombie"', 'casper') | |
4a4643c0 SR |
18 | .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).') |
19 | .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).') | |
b11bb5c3 | 20 | .option('--output-html', 'Instead of running the tests, just output the generated HTML source to STDOUT (should be used with .js tests)') |
8eb88937 | 21 | .option('-d, --debug', 'Show debug output (the "console" event) from the provider') |
b11bb5c3 | 22 | .option('-r, --relative', 'Use relative paths in the generated HTML file') |
07f514d8 | 23 | .option('--debugger <port>', 'Enable the remote debugger for CasperJS') |
2af86592 SR |
24 | .parse(process.argv); |
25 | ||
d823e895 SR |
26 | if (program.tests.length === 0) { |
27 | program.tests = fs.readdirSync(__dirname).filter(function(f) { return (/^test\.(\w|\.|-)+\.js$/).test(f); }); | |
91127741 | 28 | program.tests = program.tests.map(function (f) { return path.resolve(__dirname, f); }); // add full paths in |
d823e895 SR |
29 | console.log('using files %s', program.tests); |
30 | } | |
31 | ||
2af86592 SR |
32 | var file_paths = []; |
33 | ||
85e89916 SR |
34 | var all_js = program.tests.reduce(function(a,e) { return a && e.slice(-3) == '.js'; }, true); |
35 | ||
b11bb5c3 SR |
36 | var get_path = function (/* arguments */) { |
37 | if (program.relative) { | |
38 | return path.join.apply(null, arguments); | |
39 | } else { | |
40 | var args = Array.prototype.slice.call(arguments); | |
41 | args.unshift(__dirname, '..'); | |
42 | return path.resolve.apply(null, args); | |
43 | } | |
44 | }; | |
45 | ||
46 | var get_path_cwd = function (/* arguments */) { | |
47 | if (program.relative) { | |
48 | var part_path = path.join.apply(null, arguments); | |
49 | return path.relative(path.join(__dirname, '..'), path.resolve(process.cwd(), part_path)); | |
50 | } else { | |
51 | var args = Array.prototype.slice.call(arguments); | |
52 | args.unshift(process.cwd()); | |
53 | return path.resolve.apply(null, args); | |
54 | } | |
55 | }; | |
56 | ||
85e89916 SR |
57 | if (all_js && !program.autoInject) { |
58 | var all_modules = {}; | |
59 | ||
60 | // uses the first instance of the string 'requires local modules: ' | |
61 | program.tests.forEach(function (testname) { | |
62 | var full_path = path.resolve(process.cwd(), testname); | |
63 | var content = fs.readFileSync(full_path).toString(); | |
64 | var ind = content.indexOf('requires local modules: '); | |
65 | if (ind > -1) { | |
66 | ind += 'requires local modules: '.length; | |
67 | var eol = content.indexOf('\n', ind); | |
68 | var modules = content.slice(ind, eol).split(/,\s*/); | |
69 | modules.forEach(function (mod) { | |
ae510306 | 70 | all_modules[get_path('core/', mod) + '.js'] = 1; |
85e89916 SR |
71 | }); |
72 | } | |
d906dfc9 | 73 | |
7187bc12 | 74 | var fakes_ind = content.indexOf('requires test modules: '); |
d906dfc9 SR |
75 | if (fakes_ind > -1) { |
76 | fakes_ind += 'requires test modules: '.length; | |
77 | var fakes_eol = content.indexOf('\n', fakes_ind); | |
78 | var fakes_modules = content.slice(fakes_ind, fakes_eol).split(/,\s*/); | |
79 | fakes_modules.forEach(function (mod) { | |
b11bb5c3 | 80 | all_modules[get_path('tests/', mod) + '.js'] = 1; |
d906dfc9 SR |
81 | }); |
82 | } | |
85e89916 SR |
83 | }); |
84 | ||
85 | program.autoInject = Object.keys(all_modules); | |
86 | } | |
87 | ||
2af86592 SR |
88 | if (program.autoInject) { |
89 | var temp = require('temp'); | |
2af86592 SR |
90 | temp.track(); |
91 | ||
92 | var template = { | |
b11bb5c3 | 93 | 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>", |
8eb88937 | 94 | script_tag: function(p) { return "<script src='" + p + "'></script>"; }, |
9eca6889 | 95 | 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>" |
2af86592 SR |
96 | }; |
97 | ||
b11bb5c3 SR |
98 | template.header += "\n" + template.script_tag(get_path('node_modules/chai/chai.js')); |
99 | template.header += "\n" + template.script_tag(get_path('node_modules/mocha/mocha.js')); | |
100 | template.header += "\n" + template.script_tag(get_path('node_modules/sinon/pkg/sinon.js')); | |
101 | template.header += "\n" + template.script_tag(get_path('node_modules/sinon-chai/lib/sinon-chai.js')); | |
2af86592 SR |
102 | template.header += "\n<script>mocha.setup('bdd');</script>"; |
103 | ||
104 | ||
105 | template.header = program.autoInject.reduce(function(acc, sn) { | |
b11bb5c3 | 106 | return acc + "\n" + template.script_tag(get_path_cwd(sn)); |
2af86592 SR |
107 | }, template.header); |
108 | ||
109 | file_paths = program.tests.map(function(jsn, ind) { | |
110 | var templ = template.header; | |
111 | templ += "\n"; | |
b11bb5c3 | 112 | templ += template.script_tag(get_path_cwd(jsn)); |
2af86592 SR |
113 | templ += template.footer; |
114 | ||
115 | var tempfile = temp.openSync({ prefix: 'novnc-zombie-inject-', suffix: '-file_num-'+ind+'.html' }); | |
116 | fs.writeSync(tempfile.fd, templ); | |
117 | fs.closeSync(tempfile.fd); | |
118 | return tempfile.path; | |
119 | }); | |
120 | ||
121 | } | |
122 | else { | |
123 | file_paths = program.tests.map(function(fn) { | |
124 | return path.resolve(process.cwd(), fn); | |
125 | }); | |
126 | } | |
127 | ||
2af86592 SR |
128 | var use_ansi = false; |
129 | if (program.color) use_ansi = true; | |
130 | else if (program.disableColor) use_ansi = false; | |
131 | else if (process.stdout.isTTY) use_ansi = true; | |
132 | ||
133 | var cursor = ansi(process.stdout, { enabled: use_ansi }); | |
134 | ||
1e570156 | 135 | if (program.outputHtml) { |
1e570156 SR |
136 | file_paths.forEach(function(path, path_ind) { |
137 | fs.readFile(path, function(err, data) { | |
138 | if (err) { | |
139 | console.warn(error.stack); | |
140 | return; | |
141 | } | |
8eb88937 | 142 | |
b11bb5c3 SR |
143 | if (use_ansi) { |
144 | cursor | |
145 | .bold() | |
146 | .write(program.tests[path_ind]) | |
147 | .reset() | |
148 | .write("\n") | |
149 | .write(Array(program.tests[path_ind].length+1).join('=')) | |
150 | .write("\n\n"); | |
151 | } | |
152 | ||
1e570156 | 153 | cursor |
1e570156 | 154 | .write(data) |
b11bb5c3 | 155 | .write("\n\n"); |
1e570156 SR |
156 | }); |
157 | }); | |
158 | } | |
159 | ||
160 | if (program.generateHtml) { | |
4a4643c0 SR |
161 | var open_browser; |
162 | if (program.openInBrowser) { | |
163 | open_browser = require('open'); | |
164 | } | |
165 | ||
1e570156 SR |
166 | file_paths.forEach(function(path, path_ind) { |
167 | cursor | |
168 | .bold() | |
169 | .write(program.tests[path_ind]) | |
170 | .write(": ") | |
171 | .reset() | |
172 | .write(path) | |
173 | .write("\n"); | |
4a4643c0 SR |
174 | |
175 | if (program.openInBrowser) { | |
176 | open_browser(path); | |
177 | } | |
1e570156 | 178 | }); |
2af86592 | 179 | console.log(''); |
1e570156 | 180 | } |
2af86592 | 181 | |
1e570156 SR |
182 | if (program.generateHtml) { |
183 | process.stdin.resume(); // pause until C-c | |
184 | process.on('SIGINT', function() { | |
185 | process.stdin.pause(); // exit | |
186 | }); | |
187 | } | |
2af86592 | 188 | |
1e570156 SR |
189 | if (!program.outputHtml && !program.generateHtml) { |
190 | var failure_count = 0; | |
2af86592 | 191 | |
1e570156 | 192 | var prov = require(path.resolve(__dirname, 'run_from_console.'+program.provider+'.js')); |
2af86592 | 193 | |
2af86592 | 194 | cursor |
1e570156 SR |
195 | .write("Running tests ") |
196 | .bold() | |
197 | .write(program.tests.join(', ')) | |
2af86592 | 198 | .reset() |
1e570156 SR |
199 | .grey() |
200 | .write(' using provider '+prov.name) | |
2af86592 | 201 | .reset() |
1e570156 SR |
202 | .write("\n"); |
203 | //console.log("Running tests %s using provider %s", program.tests.join(', '), prov.name); | |
2af86592 | 204 | |
07f514d8 | 205 | var provider = prov.provide_emitter(file_paths, program.debugger); |
1e570156 SR |
206 | provider.on('test_ready', function(test_json) { |
207 | console.log(''); | |
2af86592 | 208 | |
1e570156 | 209 | filename = program.tests[test_json.file_ind]; |
2af86592 | 210 | |
1e570156 SR |
211 | cursor.bold(); |
212 | console.log('Results for %s:', filename); | |
213 | console.log(Array('Results for :'.length+filename.length+1).join('=')); | |
214 | cursor.reset(); | |
2af86592 | 215 | |
1e570156 SR |
216 | console.log(''); |
217 | ||
1e570156 | 218 | cursor |
8eb88937 | 219 | .write(''+test_json.num_tests+' tests run, ') |
1e570156 SR |
220 | .green() |
221 | .write(''+test_json.num_passes+' passed'); | |
222 | if (test_json.num_slow > 0) { | |
223 | cursor | |
224 | .reset() | |
225 | .write(' ('); | |
226 | cursor | |
227 | .yellow() | |
228 | .write(''+test_json.num_slow+' slow') | |
229 | .reset() | |
230 | .write(')'); | |
231 | } | |
232 | cursor | |
233 | .reset() | |
234 | .write(', '); | |
235 | cursor | |
236 | .red() | |
237 | .write(''+test_json.num_fails+' failed'); | |
8eb88937 SR |
238 | if (test_json.num_skipped > 0) { |
239 | cursor | |
240 | .reset() | |
241 | .write(', ') | |
242 | .grey() | |
243 | .write(''+test_json.num_skipped+' skipped'); | |
244 | } | |
1e570156 SR |
245 | cursor |
246 | .reset() | |
8eb88937 | 247 | .write(' -- duration: '+test_json.duration+"s\n"); |
1e570156 SR |
248 | |
249 | console.log(''); | |
250 | ||
251 | if (test_json.num_fails > 0 || program.printAll) { | |
b0b5fc55 SR |
252 | var extract_error_lines = function (err) { |
253 | // the split is to avoid a weird thing where in PhantomJS where we get a stack trace too | |
254 | var err_lines = err.split('\n'); | |
255 | if (err_lines.length == 1) { | |
256 | return err_lines[0]; | |
257 | } else { | |
258 | var ind; | |
259 | for (ind = 0; ind < err_lines.length; ind++) { | |
260 | var at_ind = err_lines[ind].trim().indexOf('at '); | |
261 | if (at_ind === 0) { | |
262 | break; | |
263 | } | |
264 | } | |
265 | ||
266 | return err_lines.slice(0, ind).join('\n'); | |
267 | } | |
268 | }; | |
269 | ||
1e570156 SR |
270 | var traverse_tree = function(indentation, node) { |
271 | if (node.type == 'suite') { | |
272 | if (!node.has_subfailures && !program.printAll) return; | |
273 | ||
8eb88937 | 274 | if (indentation === 0) { |
1e570156 SR |
275 | cursor.bold(); |
276 | console.log(node.name); | |
277 | console.log(Array(node.name.length+1).join('-')); | |
278 | cursor.reset(); | |
279 | } | |
280 | else { | |
281 | cursor | |
282 | .write(Array(indentation+3).join('#')) | |
283 | .bold() | |
284 | .write(' '+node.name+' ') | |
285 | .reset() | |
286 | .write(Array(indentation+3).join('#')) | |
287 | .write("\n"); | |
288 | } | |
2af86592 | 289 | |
2af86592 | 290 | console.log(''); |
1e570156 SR |
291 | |
292 | for (var i = 0; i < node.children.length; i++) { | |
293 | traverse_tree(indentation+1, node.children[i]); | |
294 | } | |
2af86592 | 295 | } |
1e570156 SR |
296 | else { |
297 | if (!node.pass) { | |
298 | cursor.magenta(); | |
299 | console.log('- failed: '+node.text+test_json.replay); | |
300 | cursor.red(); | |
b0b5fc55 | 301 | console.log(' '+extract_error_lines(node.error)); |
1e570156 SR |
302 | cursor.reset(); |
303 | console.log(''); | |
304 | } | |
305 | else if (program.printAll) { | |
8eb88937 SR |
306 | if (node.skipped) { |
307 | cursor | |
308 | .grey() | |
309 | .write('- skipped: '+node.text); | |
310 | } | |
311 | else { | |
312 | if (node.slow) cursor.yellow(); | |
313 | else cursor.green(); | |
314 | ||
315 | cursor | |
316 | .write('- pass: '+node.text) | |
317 | .grey() | |
318 | .write(' ('+node.duration+') '); | |
319 | } | |
1e570156 SR |
320 | /*if (node.slow) cursor.yellow(); |
321 | else cursor.green();*/ | |
322 | cursor | |
323 | //.write(test_json.replay) | |
324 | .reset() | |
325 | .write("\n"); | |
326 | console.log(''); | |
327 | } | |
2af86592 | 328 | } |
8eb88937 | 329 | }; |
2af86592 | 330 | |
1e570156 SR |
331 | for (var i = 0; i < test_json.suites.length; i++) { |
332 | traverse_tree(0, test_json.suites[i]); | |
333 | } | |
2af86592 | 334 | } |
2af86592 | 335 | |
8eb88937 | 336 | if (test_json.num_fails === 0) { |
1e570156 SR |
337 | cursor.fg.green(); |
338 | console.log('all tests passed :-)'); | |
339 | cursor.reset(); | |
340 | } | |
341 | }); | |
2af86592 | 342 | |
8eb88937 SR |
343 | if (program.debug) { |
344 | provider.on('console', function(line) { | |
93af721a SR |
345 | // log to stderr |
346 | console.error(line); | |
8eb88937 SR |
347 | }); |
348 | } | |
2af86592 | 349 | |
93af721a SR |
350 | provider.on('error', function(line) { |
351 | // log to stderr | |
352 | console.error('ERROR: ' + line); | |
353 | }); | |
354 | ||
1e570156 SR |
355 | /*gprom.finally(function(ph) { |
356 | ph.exit(); | |
357 | // exit with a status code that actually gives information | |
358 | if (program.exitWithFailureCount) process.exit(failure_count); | |
359 | });*/ | |
360 | } |