Skip to content

Commit f779999

Browse files
committed
Basic macro support.
This adds basic macro support as discussed in #695. It does not completely implement the spec outlined there, specifically: - The macro can only specify the title using a function in `macroFn.title`. We discussed allowing `macroFn.title` to also be a string, and allowing some form of template language for extracting a title. However, using ES2015 string templates is already pretty easy, so we may just skip this. - We discussed allowing groups of tests to be created using arrays. Both the above proposals are found in [this comment](#695 (comment)). They both enhance the implementation found in this commit, and would not break the contract. So I don't think there is anything preventing us from shipping this now.
1 parent 3407f58 commit f779999

File tree

3 files changed

+106
-4
lines changed

3 files changed

+106
-4
lines changed

lib/runner.js

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ var TestCollection = require('./test-collection');
99
function noop() {}
1010

1111
var chainableMethods = {
12-
spread: true,
1312
defaults: {
1413
type: 'test',
1514
serial: false,
@@ -55,10 +54,19 @@ function Runner(options) {
5554
util.inherits(Runner, EventEmitter);
5655
module.exports = Runner;
5756

58-
optionChain(chainableMethods, function (opts, title, fn) {
59-
if (typeof title === 'function') {
60-
fn = title;
57+
optionChain(chainableMethods, function (opts, args) {
58+
var title;
59+
var fn;
60+
var macroArgIndex;
61+
62+
if (typeof args[0] === 'string') {
63+
title = args[0];
64+
fn = args[1];
65+
macroArgIndex = 2;
66+
} else {
67+
fn = args[0];
6168
title = null;
69+
macroArgIndex = 1;
6270
}
6371

6472
if (opts.todo) {
@@ -83,6 +91,20 @@ optionChain(chainableMethods, function (opts, title, fn) {
8391
opts.exclusive = title !== null && matcher([title], this._match).length === 1;
8492
}
8593

94+
if (args.length > macroArgIndex) {
95+
args = args.slice(macroArgIndex);
96+
97+
if (!title && fn.title) {
98+
title = fn.title(args);
99+
}
100+
101+
var _fn = fn;
102+
103+
fn = function (t) {
104+
return _fn.apply(this, [t, args]);
105+
};
106+
}
107+
86108
this.tests.add({
87109
metadata: opts,
88110
fn: fn,

readme.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,33 @@ test.only.serial(...);
524524

525525
This means you can temporarily add `.skip` or `.only` at the end of a test or hook definition without having to make any other changes.
526526

527+
### Test macros
528+
529+
Additional arguments after the test function will be passed as an array to the test function. This is useful for creating reusable test macros.
530+
531+
```js
532+
function macro(t, [input, expected]) {
533+
t.is(eval(input), expected);
534+
}
535+
536+
test('2 + 2 === 4', macro, '2 + 2', 4);
537+
test('2 * 3 === 6', macro, '2 * 3', 6);
538+
```
539+
540+
You can build the test title programatically by attaching a `title` function to the macro:
541+
542+
```js
543+
function macro(t, [input, expected]) {
544+
t.is(eval(input), expected);
545+
}
546+
547+
macro.title = ([input, expected]) => `${input} === ${expected}`;
548+
549+
test(macro, '2 + 2', 4);
550+
test(macro, '2 * 3', 6);
551+
```
552+
553+
527554
### Custom assertions
528555

529556
You can use any assertion library instead of or in addition to the built-in one, provided it throws exceptions when the assertion fails.

test/runner.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -468,3 +468,56 @@ test('options.match overrides .only', function (t) {
468468
t.end();
469469
});
470470
});
471+
472+
test('additional args will be passed as an array', function (t) {
473+
t.plan(3);
474+
475+
var runner = new Runner();
476+
477+
runner.test('test1', function (avaT, args) {
478+
t.deepEqual(args, ['foo', 'bar']);
479+
}, 'foo', 'bar');
480+
481+
runner.run({}).then(function (stats) {
482+
t.is(stats.passCount, 1);
483+
t.is(stats.testCount, 1);
484+
t.end();
485+
});
486+
});
487+
488+
test('macro functions can be named by attaching a custom function', function (t) {
489+
t.plan(8);
490+
491+
var expectedTitles = [
492+
'titleA',
493+
'overridden',
494+
'titleC'
495+
];
496+
497+
var expectedArgs = [
498+
['A'],
499+
['B'],
500+
['C']
501+
];
502+
503+
function macroFn(avaT, args) {
504+
t.is(avaT.title, expectedTitles.shift());
505+
t.deepEqual(args, expectedArgs.shift());
506+
}
507+
508+
macroFn.title = function (args) {
509+
return 'title' + args[0];
510+
};
511+
512+
var runner = new Runner();
513+
514+
runner.test(macroFn, 'A');
515+
runner.test('overridden', macroFn, 'B');
516+
runner.test(macroFn, 'C');
517+
518+
runner.run({}).then(function (stats) {
519+
t.is(stats.passCount, 3);
520+
t.is(stats.testCount, 3);
521+
t.end();
522+
});
523+
});

0 commit comments

Comments
 (0)