Skip to content

Commit 35b7e5b

Browse files
committedSep 8, 2015
support running ava without arguments
1 parent f3c12c1 commit 35b7e5b

14 files changed

+1780
-142
lines changed
 

‎.gitattributes

+2
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
* text=auto
2+
*.ai binary
3+
*.psd binary

‎cli.js

+14-8
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,13 @@ var cli = meow({
1111
'Usage',
1212
' ava <file|folder|glob> [...]',
1313
'',
14-
'Example',
14+
'Examples',
15+
' ava',
1516
' ava test.js test2.js',
16-
' ava test',
17-
' ava test-*.js'
17+
' ava test-*.js',
18+
'',
19+
'Default patterns when no arguments:',
20+
'test.js test-*.js test/**'
1821
]
1922
}, {
2023
string: ['_']
@@ -37,6 +40,14 @@ function run(file) {
3740
}
3841

3942
function init(files) {
43+
if (files.length === 0) {
44+
files = [
45+
'test.js',
46+
'test-*.js',
47+
'test/**'
48+
];
49+
}
50+
4051
globby(files, function (err, files) {
4152
if (err) {
4253
console.error(err.message);
@@ -51,9 +62,4 @@ function init(files) {
5162

5263
updateNotifier({pkg: cli.pkg}).notify();
5364

54-
if (cli.input.length === 0) {
55-
console.error('Input required');
56-
process.exit(1);
57-
}
58-
5965
init(cli.input);

‎index.js

-27
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,6 @@ var Runner = require('./lib/runner');
77
var log = new Squeak({separator: ' '});
88
var runner = new Runner();
99

10-
/**
11-
* Add log types
12-
*/
13-
1410
log.type('success', {
1511
color: 'green',
1612
prefix: figures.tick
@@ -21,14 +17,6 @@ log.type('error', {
2117
prefix: figures.cross
2218
});
2319

24-
/**
25-
* Handle test
26-
*
27-
* @param {Object} err
28-
* @param {String} title
29-
* @api private
30-
*/
31-
3220
function test(err, title) {
3321
if (err) {
3422
log.error(title, chalk.red(err.message));
@@ -38,13 +26,6 @@ function test(err, title) {
3826
log.success(title);
3927
}
4028

41-
/**
42-
* Show stack for each failed test
43-
*
44-
* @param {Array} results
45-
* @api private
46-
*/
47-
4829
function stack(results) {
4930
var i = 0;
5031

@@ -61,14 +42,6 @@ function stack(results) {
6142
});
6243
}
6344

64-
/**
65-
* Show summary and exit
66-
*
67-
* @param {Object} stats
68-
* @param {Array} results
69-
* @api private
70-
*/
71-
7245
function exit(stats, results) {
7346
if (stats.testCount > 0) {
7447
log.write();

‎lib/runner.js

-51
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,6 @@ var each = require('each-async');
55
var eachSerial = require('async-each-series');
66
var Test = require('./test');
77

8-
/**
9-
* Initialize a new `Runner`
10-
*
11-
* @param {Object} opts
12-
* @api public
13-
*/
14-
158
function Runner(opts) {
169
if (!(this instanceof Runner)) {
1710
return new Runner(opts);
@@ -31,39 +24,16 @@ function Runner(opts) {
3124
util.inherits(Runner, EventEmitter);
3225
module.exports = Runner;
3326

34-
/**
35-
* Add concurrent test to `Runner`
36-
*
37-
* @param {String} title
38-
* @param {Function} cb
39-
* @api public
40-
*/
41-
4227
Runner.prototype.addTest = function (title, cb) {
4328
this.stats.testCount++;
4429
this.tests.concurrent.push(new Test(title, cb));
4530
};
4631

47-
/**
48-
* Add serial test to `Runner`
49-
*
50-
* @param {String} title
51-
* @param {Function} cb
52-
* @api public
53-
*/
54-
5532
Runner.prototype.addSerialTest = function (title, cb) {
5633
this.stats.testCount++;
5734
this.tests.serial.push(new Test(title, cb));
5835
};
5936

60-
/**
61-
* Run concurrent tests
62-
*
63-
* @param {Function} cb
64-
* @api private
65-
*/
66-
6737
Runner.prototype.concurrent = function (cb) {
6838
each(this.tests.concurrent, function (test, i, next) {
6939
test.run(function (err) {
@@ -82,13 +52,6 @@ Runner.prototype.concurrent = function (cb) {
8252
}.bind(this), cb);
8353
};
8454

85-
/**
86-
* Run serial tests
87-
*
88-
* @param {Function} cb
89-
* @api private
90-
*/
91-
9255
Runner.prototype.serial = function (cb) {
9356
eachSerial(this.tests.serial, function (test, next) {
9457
test.run(function (err) {
@@ -107,13 +70,6 @@ Runner.prototype.serial = function (cb) {
10770
}.bind(this), cb);
10871
};
10972

110-
/**
111-
* Run the `Runner`
112-
*
113-
* @param {Function} cb
114-
* @api public
115-
*/
116-
11773
Runner.prototype.run = function (cb) {
11874
var concurrent = this.tests.concurrent;
11975
var serial = this.tests.serial;
@@ -136,13 +92,6 @@ Runner.prototype.run = function (cb) {
13692
this.end(cb);
13793
};
13894

139-
/**
140-
* Handle completion
141-
*
142-
* @param {Function} cb
143-
* @api private
144-
*/
145-
14695
Runner.prototype.end = function (cb) {
14796
this.stats.passCount = this.stats.testCount - this.stats.failCount;
14897
cb(this.stats, this.results);

‎lib/test.js

+14-41
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,6 @@ var EventEmitter = require('events').EventEmitter;
55
var fnName = require('fn-name');
66
var claim = require('claim');
77

8-
/**
9-
* Initialize a new `Test`
10-
*
11-
* @param {String} title
12-
* @param {Function} fn
13-
* @api public
14-
*/
15-
168
function Test(title, fn) {
179
if (!(this instanceof Test)) {
1810
return new Test(title, fn);
@@ -56,13 +48,6 @@ Object.keys(claim).forEach(function (el) {
5648
};
5749
});
5850

59-
/**
60-
* Plan number of assertions
61-
*
62-
* @param {Number} count
63-
* @api public
64-
*/
65-
6651
Test.prototype.plan = function (count) {
6752
if (typeof count !== 'number') {
6853
throw new TypeError('Expected a number');
@@ -71,23 +56,10 @@ Test.prototype.plan = function (count) {
7156
this.planCount = count;
7257
};
7358

74-
/**
75-
* Skip test
76-
*
77-
* @api public
78-
*/
79-
8059
Test.prototype.skip = function () {
8160
this.skipTest = true;
8261
};
8362

84-
/**
85-
* Run test
86-
*
87-
* @param {Function} cb
88-
* @api public
89-
*/
90-
9163
Test.prototype.run = function (cb) {
9264
this.cb = cb;
9365

@@ -96,19 +68,26 @@ Test.prototype.run = function (cb) {
9668
}
9769

9870
try {
99-
this.fn(this);
71+
var ret = this.fn(this);
72+
73+
if (ret && typeof ret.then === 'function') {
74+
ret.then(this.exit.bind(this)).catch(function (err) {
75+
this.assertError = new assert.AssertionError({
76+
actual: err,
77+
message: 'Promise rejected → ' + err,
78+
operator: 'promise',
79+
stackStartFunction: this
80+
});
81+
82+
this.exit();
83+
}.bind(this));
84+
}
10085
} catch (err) {
10186
this.assertError = err;
10287
this.exit();
10388
}
10489
};
10590

106-
/**
107-
* End test
108-
*
109-
* @api public
110-
*/
111-
11291
Test.prototype.end = function () {
11392
if (this.endCalled) {
11493
throw new Error('.end() called more than once');
@@ -118,12 +97,6 @@ Test.prototype.end = function () {
11897
this.exit();
11998
};
12099

121-
/**
122-
* Exit test
123-
*
124-
* @api private
125-
*/
126-
127100
Test.prototype.exit = function () {
128101
if (this.planCount !== null && this.planCount !== this.assertCount) {
129102
this.assertError = new assert.AssertionError({

‎media/header.png

211 KB
Loading

‎media/header.psd

3.15 MB
Binary file not shown.

‎media/logo.ai

+1,507
Large diffs are not rendered by default.

‎media/logo.png

6.77 KB
Loading

‎media/logo.svg

+1
Loading

‎package.json

+6-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"description": "Simple concurrent test runner",
55
"license": "MIT",
66
"repository": "sindresorhus/ava",
7-
"bin": "cli.js",
7+
"homepage": "http://ava.li",
88
"author": {
99
"name": "Sindre Sorhus",
1010
"email": "sindresorhus@gmail.com",
@@ -22,11 +22,12 @@
2222
"url": "github.com/kevva"
2323
}
2424
],
25+
"bin": "cli.js",
2526
"engines": {
2627
"node": ">=0.10.0"
2728
},
2829
"scripts": {
29-
"test": "xo && node cli.js test/test.js | faucet"
30+
"test": "xo && node test/test.js | tap-dot"
3031
},
3132
"files": [
3233
"index.js",
@@ -41,6 +42,7 @@
4142
"runner",
4243
"concurrent",
4344
"parallel",
45+
"fast",
4446
"tape",
4547
"tap",
4648
"mocha",
@@ -61,7 +63,8 @@
6163
"update-notifier": "^0.5.0"
6264
},
6365
"devDependencies": {
64-
"faucet": "^0.0.1",
66+
"pinkie-promise": "^1.0.0",
67+
"tap-dot": "^1.0.0",
6568
"tape": "^4.0.0",
6669
"xo": "*"
6770
}

‎readme.md

+185-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1-
# ava [![Build Status](https://travis-ci.org/sindresorhus/ava.svg?branch=master)](https://travis-ci.org/sindresorhus/ava)
1+
# ![AVA](media/header.png)
22

33
> Simple concurrent test runner
44
5+
[![Build Status](https://travis-ci.org/sindresorhus/ava.svg?branch=master)](https://travis-ci.org/sindresorhus/ava)
6+
7+
Even though JavaScript is single-threaded, IO in Node.js can happen in parallel due to its async nature. AVA takes advantage of this and runs your tests concurrently, which is especially beneficial for IO heavy tests. [Switching](https://github.com/sindresorhus/pageres/commit/663be15acb3dd2eb0f71b1956ef28c2cd3fdeed0) from Mocha to AVA in Pageres brought the test time down from 31 sec to 11 sec. Having tests run concurrently forces you to write atomic tests, meaning tests that don't depend on global state or the state of other tests, which is a great thing!
8+
59

610
## Install
711

@@ -12,45 +16,214 @@ $ npm install --save-dev ava
1216

1317
## Usage
1418

15-
##### Add it to `package.json`
19+
##### Add it to package.json
1620

1721
```json
1822
{
1923
"scripts": {
20-
"test": "ava test.js"
24+
"test": "ava"
2125
}
2226
}
2327
```
2428

25-
Ava accepts files/folders/globs.
26-
2729

2830
##### Create your test file
2931

3032
```js
3133
var test = require('ava');
3234

33-
test('test something', function (t) {
34-
t.assert(true);
35-
t.is('unicorn', 'unicorn');
35+
test('foo', function (t) {
36+
t.pass();
3637
t.end();
3738
});
39+
40+
test('bar', function (t) {
41+
t.plan(2)
42+
43+
setTimeout(function () {
44+
t.is('bar', 'bar');
45+
t.same(['a', 'b'], ['a', 'b']);
46+
}, 100);
47+
});
3848
```
3949

50+
<img src="screenshot.png" width="150" align="right">
51+
4052
##### Run it
4153

4254
```
4355
$ npm test
4456
```
4557

4658

47-
## Credit
59+
## CLI
60+
61+
```
62+
$ ava --help
63+
64+
Usage
65+
ava <file|folder|glob> [...]
66+
67+
Examples
68+
ava
69+
ava test.js test2.js
70+
ava test-*.js
71+
72+
Default patterns when no arguments:
73+
test.js test-*.js test/**
74+
```
75+
76+
77+
## Documentation
78+
79+
Test files are just normal Node.js scripts and can be run with `$ node test.js`. However, using the CLI is preferred for simplicity and future [parallelism support](https://github.com/sindresorhus/ava/issues/1).
80+
81+
Tests are run async and require you to either set planned assertions `t.plan(1)`, explicitly end the test when done `t.end()`, or return a promise.
82+
83+
You have to define all tests synchronously, meaning you can't define a test in the next tick, e.g. inside a `setTimeout`.
84+
85+
### Test anatomy
86+
87+
To create a test, you just call the `test` function you require'd from AVA and pass in an optional test name and a callback function containing the test execution. The passed callback function is given the context as the first argument where you can call the different AVA methods and assertions.
88+
89+
```js
90+
test('name', function (t) {
91+
t.pass();
92+
t.end();
93+
});
94+
```
95+
96+
### Optional test name
97+
98+
Naming a test is optional, but you're recommended to use one if you have more than one test.
99+
100+
```js
101+
test(function (t) {
102+
t.end();
103+
});
104+
```
105+
106+
You can also choose to use a named function instead:
107+
108+
```js
109+
test(function name(t) {
110+
t.end();
111+
});
112+
```
113+
114+
### Planned assertions
115+
116+
Planned assertions are useful for being able to assert that all async actions happened and catch bugs where too many assertions happen. It also comes with the benefit of not having to manually end the test.
117+
118+
This will result in a passed test:
119+
120+
```js
121+
test(function (t) {
122+
t.plan(1);
123+
124+
setTimeout(function () {
125+
t.pass();
126+
}, 100);
127+
});
128+
```
129+
130+
And this will result in an error because the code called more assertions than planned:
131+
132+
```js
133+
test(function (t) {
134+
t.plan(1);
135+
136+
t.pass();
137+
138+
setTimeout(function () {
139+
t.pass();
140+
}, 100);
141+
});
142+
```
143+
144+
### Promise support
145+
146+
If you return a promise in the test you don't need to explicitly end the test as it will end when the promise resolves.
147+
148+
```js
149+
test(function (t) {
150+
return somePromise().then(function (result) {
151+
t.is(result, 'unicorn');
152+
});
153+
});
154+
```
155+
156+
157+
### Serial test execution
158+
159+
While concurrency is awesome, there are some things that can't be done concurrently. In these rare cases, you can call `test.serial`, which will force those tests to run serially before the concurrent ones.
160+
161+
```js
162+
test.serial(function (t) {
163+
t.end();
164+
});
165+
```
166+
167+
168+
## API
169+
170+
### test([name], body)
171+
### test.serial([name], body)
172+
173+
#### name
48174

49-
[![Sindre Sorhus](http://gravatar.com/avatar/d36a92237c75c5337c17b60d90686bf9?s=144)](http://sindresorhus.com) | [![Kevin Mårtensson](http://gravatar.com/avatar/48fa294e3cd41680b80d3ed6345c7b4d?s=144)](https://github.com/kevva)
50-
---|---
51-
[Sindre Sorhus](http://sindresorhus.com) | [Kevin Mårtensson](https://github.com/kevva)
175+
Type: `string`
176+
177+
Test name.
178+
179+
#### body(context)
180+
181+
Type: `function`
182+
183+
Should contain the actual test.
184+
185+
##### context
186+
187+
Passed into the test function and contains the different AVA methods and assertions.
188+
189+
See the [`claim` docs](https://github.com/kevva/claim#api) for supported assertions.
190+
191+
###### plan(count)
192+
193+
Plan how many assertion there are in the test. The test will fail if the actual assertion count doesn't match planned assertions. When planned assertions are used you don't need to explicitly end the test.
194+
195+
Be aware that this doesn't work with custom assert modules. You must then call `.end()` explicitly.
196+
197+
###### end()
198+
199+
End the test. Use this when `plan()` is not used.
200+
201+
202+
## Tips
203+
204+
### Temp files
205+
206+
Running tests concurrently comes with some challenges, doing IO is one. Usually, serial tests just create temp directories in the current test directory and cleans it up at the end. This won't work when you run tests concurrently as tests will conflict with each other. The correct way to do it is to use a new temp directory for each test. The [`tempfile`](https://github.com/sindresorhus/tempfile) and [`temp-write`](https://github.com/sindresorhus/temp-write) modules can be helpful.
207+
208+
209+
## FAQ
210+
211+
### Why not `mocha`, `tape`, `node-tap`?
212+
213+
Mocha requires you to use implicit globals like `describe` and `it`, too unopinionated, bloated, synchronous by default, serial test execution, and slow. Tape and node-tap are pretty good. AVA is highly inspired by their syntax. However, they both execute tests serially and they've made [TAP](https://testanything.org) a first-class citizen which has IMHO made their codebases a bit convoluted and coupled. TAP output is hard to read so you always end up using an external tap reporter. AVA is highly opinionated and concurrent. It comes with a default simple reporter and will in the future support TAP through a reporter.
52214

53215

54216
## License
55217

56218
MIT © [Sindre Sorhus](http://sindresorhus.com)
219+
220+
221+
<div align="center">
222+
<br>
223+
<br>
224+
<br>
225+
<img src="https://cdn.rawgit.com/sindresorhus/ava/fe1cea1ca3d2c8518c0cc39ec8be592beab90558/media/logo.svg" width="200" alt="AVA">
226+
<br>
227+
<br>
228+
</div>
229+

‎screenshot.png

4.43 KB
Loading

‎test/test.js

+51
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use strict';
22
var test = require('tape');
3+
var Promise = require('pinkie-promise');
34
var ava = require('../lib/test');
45
var Runner = require('../lib/runner');
56

@@ -292,3 +293,53 @@ test.skip('skip test with `.skip()`', function (t) {
292293
t.end();
293294
});
294295
});
296+
297+
function promisePass() {
298+
return new Promise(function (resolve) {
299+
setImmediate(resolve);
300+
});
301+
}
302+
303+
function promiseFail() {
304+
return new Promise(function (resolve, reject) {
305+
setImmediate(function () {
306+
reject(new Error('unicorn'));
307+
});
308+
});
309+
}
310+
311+
test('promise support - assert pass', function (t) {
312+
ava(function (a) {
313+
return promisePass().then(function () {
314+
a.pass();
315+
});
316+
}).run(function () {
317+
t.is(this.assertCount, 1);
318+
t.end();
319+
});
320+
});
321+
322+
test('promise support - assert fail', function (t) {
323+
ava(function (a) {
324+
return promisePass().then(function () {
325+
// TODO: replace with `a.fail()` when it's available
326+
a.true(false);
327+
});
328+
}).run(function (err) {
329+
t.true(err);
330+
t.is(err.name, 'AssertionError');
331+
t.end();
332+
});
333+
});
334+
335+
test('promise support - reject', function (t) {
336+
ava(function (a) {
337+
return promiseFail().then(function () {
338+
a.pass();
339+
});
340+
}).run(function (err) {
341+
t.true(err);
342+
t.is(err.name, 'AssertionError');
343+
t.end();
344+
});
345+
});

0 commit comments

Comments
 (0)
Please sign in to comment.