Skip to content

Commit 7884d34

Browse files
committed
Wrap help text to the terminal width
1 parent 9eba295 commit 7884d34

File tree

3 files changed

+128
-24
lines changed

3 files changed

+128
-24
lines changed

bin/jasmine.js

+7-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const path = require('path');
44
const os = require('os');
5+
const process = require('process');
56
const Command = require('../lib/command');
67
const Jasmine = require('../lib/jasmine');
78
const ParallelRunner = require("../lib/parallel_runner");
@@ -11,7 +12,12 @@ const command = new Command(path.resolve(), examplesDir, {
1112
Jasmine,
1213
ParallelRunner,
1314
print: console.log,
15+
terminalColumns: process.stdout.columns,
1416
platform: os.platform,
1517
});
1618

17-
command.run(process.argv.slice(2));
19+
command.run(process.argv.slice(2))
20+
.catch(e => {
21+
console.error(e);
22+
process.exit(1);
23+
});

lib/command.js

+74-22
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const subCommands = {
2727
};
2828

2929
function Command(projectBaseDir, examplesDir, deps) {
30-
const {print, platform, Jasmine, ParallelRunner} = deps;
30+
const {print, platform, terminalColumns, Jasmine, ParallelRunner} = deps;
3131
const isWindows = platform() === 'win32';
3232

3333
this.projectBaseDir = isWindows ? unWindows(projectBaseDir) : projectBaseDir;
@@ -54,7 +54,8 @@ function Command(projectBaseDir, examplesDir, deps) {
5454
projectBaseDir,
5555
specDir: command.specDir,
5656
examplesDir: examplesDir,
57-
print
57+
print,
58+
terminalColumns
5859
});
5960
} else {
6061
const options = parseOptions(args, isWindows);
@@ -66,7 +67,7 @@ function Command(projectBaseDir, examplesDir, deps) {
6667
}
6768

6869
print('');
69-
help({print: print});
70+
help({print, terminalColumns});
7071
} else {
7172
await runJasmine(Jasmine, ParallelRunner, projectBaseDir, options);
7273
}
@@ -284,39 +285,90 @@ function installExamples(options) {
284285
);
285286
}
286287

287-
function help(options) {
288-
const print = options.print;
289-
print('Usage: jasmine [command] [options] [files] [--]');
288+
function help(deps) {
289+
const print = deps.print;
290+
let terminalColumns = deps.terminalColumns || 80;
291+
292+
print(wrap(terminalColumns, 'Usage: jasmine [command] [options] [files] [--]'));
290293
print('');
291294
print('Commands:');
292295
Object.keys(subCommands).forEach(function(cmd) {
293296
let commandNameText = cmd;
294297
if(subCommands[cmd].alias) {
295298
commandNameText = commandNameText + ',' + subCommands[cmd].alias;
296299
}
297-
print('%s\t%s', lPad(commandNameText, 10), subCommands[cmd].description);
300+
print(wrapWithIndent(terminalColumns, lPad(commandNameText, 10) + ' ', subCommands[cmd].description));
298301
});
299302
print('');
300-
print('If no command is given, jasmine specs will be run');
303+
print(wrap(terminalColumns, 'If no command is given, Jasmine specs will be run.'));
301304
print('');
302305
print('');
303306

304307
print('Options:');
305-
print('%s\tRun in parallel with N workers', lPad('--parallel=N', 18));
306-
print('%s\tRun in parallel with an automatically chosen number of workers', lPad('--parallel=auto', 18));
307-
print('%s\tturn off color in spec output', lPad('--no-color', 18));
308-
print('%s\tforce turn on color in spec output', lPad('--color', 18));
309-
print('%s\tfilter specs to run only those that match the given string', lPad('--filter=', 18));
310-
print('%s\tload helper files that match the given string', lPad('--helper=', 18));
311-
print('%s\tload module that match the given string', lPad('--require=', 18));
312-
print('%s\tstop Jasmine execution on spec failure', lPad('--fail-fast', 18));
313-
print('%s\tpath to your optional jasmine.json', lPad('--config=', 18));
314-
print('%s\tpath to reporter to use instead of the default Jasmine reporter', lPad('--reporter=', 18));
315-
print('%s\tprint information that may be useful for debugging configuration', lPad('--verbose', 18));
316-
print('%s\tmarker to signal the end of options meant for Jasmine', lPad('--', 18));
308+
309+
const options = [
310+
{ syntax: '--parallel=N', help: 'Run in parallel with N workers' },
311+
{ syntax: '--parallel=auto', help: 'Run in parallel with an automatically chosen number of workers' },
312+
{ syntax: '--no-color', help: 'turn off color in spec output' },
313+
{ syntax: '--color', help: 'force turn on color in spec output' },
314+
{ syntax: '--filter=', help: 'filter specs to run only those that match the given string' },
315+
{ syntax: '--helper=', help: 'load helper files that match the given string' },
316+
{ syntax: '--require=', help: 'load module that matches the given string' },
317+
{ syntax: '--fail-fast', help: 'stop Jasmine execution on spec failure' },
318+
{ syntax: '--config=', help: 'path to the Jasmine configuration file' },
319+
{ syntax: '--reporter=', help: 'path to reporter to use instead of the default Jasmine reporter' },
320+
{ syntax: '--verbose', help: 'print information that may be useful for debugging configuration' },
321+
{ syntax: '--', help: 'marker to signal the end of options meant for Jasmine' },
322+
];
323+
324+
for (const o of options) {
325+
print(wrapWithIndent(terminalColumns, lPad(o.syntax, 18) + ' ', o.help));
326+
}
327+
317328
print('');
318-
print('The given arguments take precedence over options in your jasmine.json');
319-
print('The path to your optional jasmine.json can also be configured by setting the JASMINE_CONFIG_PATH environment variable');
329+
print(wrap(terminalColumns,
330+
'The given arguments take precedence over options in your jasmine.json.'));
331+
print(wrap(terminalColumns,
332+
'The path to your optional jasmine.json can also be configured by setting the JASMINE_CONFIG_PATH environment variable.'));
333+
}
334+
335+
function wrapWithIndent(cols, prefix, suffix) {
336+
const lines = wrap2(cols - prefix.length, suffix);
337+
const indent = lPad('', prefix.length);
338+
return prefix + lines.join('\n' + indent);
339+
}
340+
341+
function wrap(cols, input) {
342+
return wrap2(cols, input).join('\n');
343+
}
344+
345+
function wrap2(cols, input) {
346+
let lines = [];
347+
let start = 0;
348+
349+
while (start < input.length) {
350+
const splitAt = indexOfLastSpaceInRange(start, start + cols, input);
351+
352+
if (splitAt === -1 || input.length - start <= cols) {
353+
lines.push(input.substring(start));
354+
break;
355+
} else {
356+
lines.push(input.substring(start, splitAt));
357+
start = splitAt + 1;
358+
}
359+
}
360+
361+
return lines;
362+
}
363+
364+
function indexOfLastSpaceInRange(start, end, s) {
365+
for (let i = end; i >= start; i--) {
366+
if (s[i] === ' ') {
367+
return i;
368+
}
369+
}
370+
371+
return -1;
320372
}
321373

322374
function version(options) {

spec/command_spec.js

+47-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ describe('command', function() {
4444
let output = "";
4545
return {
4646
print: function(str) {
47-
output += str;
47+
output += str + '\n';
4848
},
4949
getOutput: function() {
5050
return output;
@@ -565,6 +565,52 @@ describe('command', function() {
565565
});
566566
});
567567
});
568+
569+
describe('help', function() {
570+
it('wraps text to the terminal width', async function() {
571+
this.command = new Command(projectBaseDir, '', {
572+
print: this.out.print,
573+
terminalColumns: 50,
574+
platform() {
575+
return 'arbitrary';
576+
}
577+
});
578+
579+
await this.command.run(['help']);
580+
581+
const output = this.out.getOutput();
582+
expect(output).toContain('version,-v show jasmine and jasmine-core\n' +
583+
' versions\n');
584+
expect(output).toContain(' --parallel=auto Run in parallel with an\n' +
585+
' automatically chosen number\n' +
586+
' of workers\n' +
587+
' --no-color turn off color in spec\n' +
588+
' output\n' +
589+
' --color force turn on color in spec\n' +
590+
' output\n');
591+
expect(output).toContain('The given arguments take precedence over options\n' +
592+
'in your jasmine.json.\n' +
593+
'The path to your optional jasmine.json can also be\n' +
594+
'configured by setting the JASMINE_CONFIG_PATH\n' +
595+
'environment variable.\n');
596+
});
597+
598+
it('wraps text to 80 columns when the terminal width is unknown', function() {
599+
this.command = new Command(projectBaseDir, '', {
600+
print: this.out.print,
601+
terminalColumns: undefined,
602+
platform() {
603+
return 'arbitrary';
604+
}
605+
});
606+
607+
this.command.run(['help']);
608+
609+
const output = this.out.getOutput();
610+
expect(output).toContain(' --parallel=auto Run in parallel with an automatically chosen number of\n' +
611+
' workers\n');
612+
});
613+
});
568614
});
569615

570616
// Adapted from Sindre Sorhus's escape-string-regexp (MIT license)

0 commit comments

Comments
 (0)