ava 0.16.0 → 0.18.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/concurrent.js CHANGED
@@ -1,96 +1,83 @@
1
1
  'use strict';
2
- var Promise = require('bluebird');
3
- var isPromise = require('is-promise');
4
- var AvaError = require('./ava-error');
5
-
6
- function noop() {}
7
-
8
- module.exports = Concurrent;
2
+ const Promise = require('bluebird');
3
+ const isPromise = require('is-promise');
4
+ const autoBind = require('auto-bind');
5
+ const AvaError = require('./ava-error');
6
+
7
+ class Concurrent {
8
+ constructor(tests, bail) {
9
+ if (!Array.isArray(tests)) {
10
+ throw new TypeError('Expected an array of tests');
11
+ }
9
12
 
10
- function Concurrent(tests, bail) {
11
- if (!(this instanceof Concurrent)) {
12
- throw new TypeError('Class constructor Concurrent cannot be invoked without \'new\'');
13
- }
13
+ this.results = [];
14
+ this.passed = true;
15
+ this.reason = null;
16
+ this.tests = tests;
17
+ this.bail = bail || false;
14
18
 
15
- if (!Array.isArray(tests)) {
16
- throw new TypeError('Expected an array of tests');
19
+ autoBind(this);
17
20
  }
21
+ run() {
22
+ let results;
18
23
 
19
- this.results = [];
20
- this.passed = true;
21
- this.reason = null;
22
- this.tests = tests;
23
- this.bail = bail || false;
24
+ try {
25
+ results = this.tests.map(this._runTest);
26
+ } catch (err) {
27
+ if (err instanceof AvaError) {
28
+ return this._results();
29
+ }
24
30
 
25
- Object.keys(Concurrent.prototype).forEach(function (key) {
26
- this[key] = this[key].bind(this);
27
- }, this);
28
- }
29
-
30
- Concurrent.prototype.run = function () {
31
- var results;
32
-
33
- try {
34
- results = this.tests.map(this._runTest);
35
- } catch (err) {
36
- if (err instanceof AvaError) {
37
- return this._results();
31
+ throw err;
38
32
  }
39
33
 
40
- throw err;
41
- }
34
+ const isAsync = results.some(isPromise);
42
35
 
43
- var isAsync = results.some(isPromise);
36
+ if (isAsync) {
37
+ return Promise.all(results)
38
+ .catch(AvaError, () => {})
39
+ .then(this._results);
40
+ }
44
41
 
45
- if (isAsync) {
46
- return Promise.all(results)
47
- .catch(AvaError, noop)
48
- .then(this._results);
42
+ return this._results();
49
43
  }
44
+ _runTest(test, index) {
45
+ const result = test.run();
50
46
 
51
- return this._results();
52
- };
53
-
54
- Concurrent.prototype._runTest = function (test, index) {
55
- var result = test.run();
56
-
57
- if (isPromise(result)) {
58
- var self = this;
47
+ if (isPromise(result)) {
48
+ return result.then(result => this._addResult(result, index));
49
+ }
59
50
 
60
- return result.then(function (result) {
61
- return self._addResult(result, index);
62
- });
51
+ return this._addResult(result, index);
63
52
  }
53
+ _addResult(result, index) {
54
+ // Always save result when not in bail mode or all previous tests pass
55
+ if ((this.bail && this.passed) || !this.bail) {
56
+ this.results[index] = result;
57
+ }
64
58
 
65
- return this._addResult(result, index);
66
- };
67
-
68
- Concurrent.prototype._addResult = function (result, index) {
69
- // always save result when not in bail mode or all previous tests pass
70
- if ((this.bail && this.passed) || !this.bail) {
71
- this.results[index] = result;
72
- }
59
+ if (result.passed === false) {
60
+ this.passed = false;
73
61
 
74
- if (result.passed === false) {
75
- this.passed = false;
62
+ // Only set reason once
63
+ if (!this.reason) {
64
+ this.reason = result.reason;
65
+ }
76
66
 
77
- // only set reason once
78
- if (!this.reason) {
79
- this.reason = result.reason;
67
+ if (this.bail) {
68
+ throw new AvaError('Error in Concurrent while in bail mode');
69
+ }
80
70
  }
81
71
 
82
- if (this.bail) {
83
- throw new AvaError('Error in Concurrent while in bail mode');
84
- }
72
+ return result;
85
73
  }
74
+ _results() {
75
+ return {
76
+ passed: this.passed,
77
+ reason: this.reason,
78
+ result: this.results
79
+ };
80
+ }
81
+ }
86
82
 
87
- return result;
88
- };
89
-
90
- Concurrent.prototype._results = function () {
91
- return {
92
- passed: this.passed,
93
- reason: this.reason,
94
- result: this.results
95
- };
96
- };
83
+ module.exports = Concurrent;
@@ -1,68 +1,76 @@
1
1
  'use strict';
2
+ const dotProp = require('dot-prop');
2
3
 
3
- module.exports = enhanceAssert;
4
- module.exports.formatter = formatter;
5
-
6
- module.exports.PATTERNS = [
4
+ // When adding patterns, don't forget to add to
5
+ // https://github.com/avajs/babel-preset-transform-test-files/blob/master/espower-patterns.json
6
+ // Then release a new version of that preset and bump the SemVer range here.
7
+ const PATTERNS = [
7
8
  't.truthy(value, [message])',
8
9
  't.falsy(value, [message])',
9
10
  't.true(value, [message])',
10
11
  't.false(value, [message])',
11
- 't.is(value, expected, [message])',
12
- 't.not(value, expected, [message])',
13
- 't.deepEqual(value, expected, [message])',
14
- 't.notDeepEqual(value, expected, [message])',
15
12
  't.regex(contents, regex, [message])',
16
- 't.notRegex(contents, regex, [message])',
17
- // deprecated apis
18
- 't.ok(value, [message])',
19
- 't.notOk(value, [message])',
20
- 't.same(value, expected, [message])',
21
- 't.notSame(value, expected, [message])'
13
+ 't.notRegex(contents, regex, [message])'
22
14
  ];
23
15
 
24
- module.exports.NON_ENHANCED_PATTERNS = [
16
+ const NON_ENHANCED_PATTERNS = [
25
17
  't.pass([message])',
26
18
  't.fail([message])',
27
19
  't.throws(fn, [message])',
28
20
  't.notThrows(fn, [message])',
29
- 't.ifError(error, [message])'
21
+ 't.ifError(error, [message])',
22
+ 't.snapshot(contents, [message])',
23
+ 't.is(value, expected, [message])',
24
+ 't.not(value, expected, [message])',
25
+ 't.deepEqual(value, expected, [message])',
26
+ 't.notDeepEqual(value, expected, [message])'
30
27
  ];
31
28
 
32
- function enhanceAssert(opts) {
33
- var empower = require('empower-core');
34
-
35
- var enhanced = empower(
36
- opts.assert,
37
- {
38
- destructive: false,
39
- onError: opts.onError,
40
- onSuccess: opts.onSuccess,
41
- patterns: module.exports.PATTERNS,
42
- wrapOnlyPatterns: module.exports.NON_ENHANCED_PATTERNS,
43
- bindReceiver: false
44
- }
45
- );
29
+ const enhanceAssert = opts => {
30
+ const empower = require('empower-core');
31
+ const enhanced = empower(opts.assert, {
32
+ destructive: false,
33
+ onError: opts.onError,
34
+ onSuccess: opts.onSuccess,
35
+ patterns: PATTERNS,
36
+ wrapOnlyPatterns: NON_ENHANCED_PATTERNS,
37
+ bindReceiver: false
38
+ });
46
39
 
47
40
  return enhanced;
48
- }
41
+ };
49
42
 
50
- function formatter() {
51
- var createFormatter = require('power-assert-context-formatter');
52
- var SuccinctRenderer = require('power-assert-renderer-succinct');
53
- var AssertionRenderer = require('power-assert-renderer-assertion');
43
+ const isRangeMatch = (a, b) => {
44
+ return (a[0] === b[0] && a[1] === b[1]) ||
45
+ (a[0] > b[0] && a[0] < b[1]) ||
46
+ (a[1] > b[0] && a[1] < b[1]);
47
+ };
54
48
 
55
- return createFormatter({
56
- renderers: [
57
- {
58
- ctor: AssertionRenderer
59
- },
60
- {
61
- ctor: SuccinctRenderer,
62
- options: {
63
- maxDepth: 3
64
- }
65
- }
66
- ]
67
- });
68
- }
49
+ const computeStatement = (tokens, range) => {
50
+ return tokens
51
+ .filter(token => isRangeMatch(token.range, range))
52
+ .map(token => token.value === undefined ? token.type.label : token.value)
53
+ .join('');
54
+ };
55
+
56
+ const getNode = (ast, path) => dotProp.get(ast, path.replace(/\//g, '.'));
57
+
58
+ const formatter = () => {
59
+ return context => {
60
+ const ast = JSON.parse(context.source.ast);
61
+ const tokens = JSON.parse(context.source.tokens);
62
+ const args = context.args[0].events;
63
+
64
+ return args
65
+ .map(arg => {
66
+ const range = getNode(ast, arg.espath).range;
67
+ return [computeStatement(tokens, range), arg.value];
68
+ })
69
+ .reverse();
70
+ };
71
+ };
72
+
73
+ module.exports = enhanceAssert;
74
+ module.exports.PATTERNS = PATTERNS;
75
+ module.exports.NON_ENHANCED_PATTERNS = NON_ENHANCED_PATTERNS;
76
+ module.exports.formatter = formatter;
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+ const stackLineRegex = /^.+ \(.+:[0-9]+:[0-9]+\)$/;
3
+
4
+ module.exports = stack => {
5
+ return stack
6
+ .split('\n')
7
+ .filter(line => stackLineRegex.test(line))
8
+ .map(line => line.trim())
9
+ .join('\n');
10
+ };
package/lib/fork.js CHANGED
@@ -1,39 +1,34 @@
1
1
  'use strict';
2
- var childProcess = require('child_process');
3
- var path = require('path');
4
- var fs = require('fs');
5
- var objectAssign = require('object-assign');
6
- var Promise = require('bluebird');
7
- var debug = require('debug')('ava');
8
- var AvaError = require('./ava-error');
9
- var doSend = require('./send');
2
+ const childProcess = require('child_process');
3
+ const path = require('path');
4
+ const fs = require('fs');
5
+ const Promise = require('bluebird');
6
+ const debug = require('debug')('ava');
7
+ const AvaError = require('./ava-error');
10
8
 
11
9
  if (fs.realpathSync(__filename) !== __filename) {
12
- console.warn(
13
- 'WARNING: `npm link ava` and the `--preserve-symlink` flag are incompatible. ' +
14
- 'We have detected that AVA is linked via `npm link`, and that you are using either ' +
15
- 'an early version of Node 6, or the `--preserve-symlink` flag. This breaks AVA. ' +
16
- 'You should upgrade to Node 6.2.0+, avoid the `--preserve-symlink` flag, or avoid using `npm link ava`.'
17
- );
10
+ console.warn('WARNING: `npm link ava` and the `--preserve-symlink` flag are incompatible. We have detected that AVA is linked via `npm link`, and that you are using either an early version of Node 6, or the `--preserve-symlink` flag. This breaks AVA. You should upgrade to Node 6.2.0+, avoid the `--preserve-symlink` flag, or avoid using `npm link ava`.');
18
11
  }
19
12
 
20
- var env = process.env;
13
+ let env = process.env;
21
14
 
22
- // ensure NODE_PATH paths are absolute
15
+ // Ensure NODE_PATH paths are absolute
23
16
  if (env.NODE_PATH) {
24
- env = objectAssign({}, env);
17
+ env = Object.assign({}, env);
25
18
 
26
19
  env.NODE_PATH = env.NODE_PATH
27
20
  .split(path.delimiter)
28
- .map(function (x) {
29
- return path.resolve(x);
30
- })
21
+ .map(x => path.resolve(x))
31
22
  .join(path.delimiter);
32
23
  }
33
24
 
34
- module.exports = function (file, opts) {
35
- opts = objectAssign({
36
- file: file,
25
+ // In case the test file imports a different AVA install,
26
+ // the presence of this variable allows it to require this one instead
27
+ env.AVA_PATH = path.resolve(__dirname, '..');
28
+
29
+ module.exports = (file, opts, execArgv) => {
30
+ opts = Object.assign({
31
+ file,
37
32
  baseDir: process.cwd(),
38
33
  tty: process.stdout.isTTY ? {
39
34
  columns: process.stdout.columns,
@@ -41,37 +36,44 @@ module.exports = function (file, opts) {
41
36
  } : false
42
37
  }, opts);
43
38
 
44
- var ps = childProcess.fork(path.join(__dirname, 'test-worker.js'), [JSON.stringify(opts)], {
45
- cwd: path.dirname(file),
39
+ const args = [JSON.stringify(opts), opts.color ? '--color' : '--no-color'];
40
+
41
+ const ps = childProcess.fork(path.join(__dirname, 'test-worker.js'), args, {
42
+ cwd: opts.projectDir,
46
43
  silent: true,
47
- env: env
44
+ env,
45
+ execArgv: execArgv || process.execArgv
48
46
  });
49
47
 
50
- var relFile = path.relative('.', file);
48
+ const relFile = path.relative('.', file);
51
49
 
52
- var exiting = false;
53
- var send = function (ps, name, data) {
50
+ let exiting = false;
51
+ const send = (name, data) => {
54
52
  if (!exiting) {
55
53
  // This seems to trigger a Node bug which kills the AVA master process, at
56
54
  // least while running AVA's tests. See
57
55
  // <https://github.com/novemberborn/_ava-tap-crash> for more details.
58
- doSend(ps, name, data);
56
+ ps.send({
57
+ name: `ava-${name}`,
58
+ data,
59
+ ava: true
60
+ });
59
61
  }
60
62
  };
61
63
 
62
- var testResults = [];
63
- var results;
64
+ const testResults = [];
65
+ let results;
64
66
 
65
- var promise = new Promise(function (resolve, reject) {
67
+ const promise = new Promise((resolve, reject) => {
66
68
  ps.on('error', reject);
67
69
 
68
- // emit `test` and `stats` events
69
- ps.on('message', function (event) {
70
+ // Emit `test` and `stats` events
71
+ ps.on('message', event => {
70
72
  if (!event.ava) {
71
73
  return;
72
74
  }
73
75
 
74
- event.name = event.name.replace(/^ava\-/, '');
76
+ event.name = event.name.replace(/^ava-/, '');
75
77
  event.data.file = relFile;
76
78
 
77
79
  debug('ipc %s:\n%o', event.name, event.data);
@@ -79,36 +81,36 @@ module.exports = function (file, opts) {
79
81
  ps.emit(event.name, event.data);
80
82
  });
81
83
 
82
- ps.on('test', function (props) {
84
+ ps.on('test', props => {
83
85
  testResults.push(props);
84
86
  });
85
87
 
86
- ps.on('results', function (data) {
88
+ ps.on('results', data => {
87
89
  results = data;
88
90
  data.tests = testResults;
89
- send(ps, 'teardown');
91
+ send('teardown');
90
92
  });
91
93
 
92
- ps.on('exit', function (code, signal) {
94
+ ps.on('exit', (code, signal) => {
93
95
  if (code > 0) {
94
- return reject(new AvaError(relFile + ' exited with a non-zero exit code: ' + code));
96
+ return reject(new AvaError(`${relFile} exited with a non-zero exit code: ${code}`));
95
97
  }
96
98
 
97
99
  if (code === null && signal) {
98
- return reject(new AvaError(relFile + ' exited due to ' + signal));
100
+ return reject(new AvaError(`${relFile} exited due to ${signal}`));
99
101
  }
100
102
 
101
103
  if (results) {
102
104
  resolve(results);
103
105
  } else {
104
- reject(new AvaError('Test results were not received from ' + relFile));
106
+ reject(new AvaError(`Test results were not received from ${relFile}`));
105
107
  }
106
108
  });
107
109
 
108
- ps.on('no-tests', function (data) {
109
- send(ps, 'teardown');
110
+ ps.on('no-tests', data => {
111
+ send('teardown');
110
112
 
111
- var message = 'No tests found in ' + relFile;
113
+ let message = `No tests found in ${relFile}`;
112
114
 
113
115
  if (!data.avaRequired) {
114
116
  message += ', make sure to import "ava" at the top of your test file';
@@ -118,58 +120,55 @@ module.exports = function (file, opts) {
118
120
  });
119
121
  });
120
122
 
121
- // teardown finished, now exit
122
- ps.on('teardown', function () {
123
- send(ps, 'exit');
123
+ // Teardown finished, now exit
124
+ ps.on('teardown', () => {
125
+ send('exit');
124
126
  exiting = true;
125
127
  });
126
128
 
127
- // uncaught exception in fork, need to exit
128
- ps.on('uncaughtException', function () {
129
- send(ps, 'teardown');
129
+ // Uncaught exception in fork, need to exit
130
+ ps.on('uncaughtException', () => {
131
+ send('teardown');
130
132
  });
131
133
 
132
- ps.stdout.on('data', function (data) {
134
+ ps.stdout.on('data', data => {
133
135
  ps.emit('stdout', data);
134
136
  });
135
137
 
136
- ps.stderr.on('data', function (data) {
138
+ ps.stderr.on('data', data => {
137
139
  ps.emit('stderr', data);
138
140
  });
139
141
 
140
142
  promise.on = function () {
141
143
  ps.on.apply(ps, arguments);
142
-
143
144
  return promise;
144
145
  };
145
146
 
146
- promise.send = function (name, data) {
147
- send(ps, name, data);
148
-
147
+ promise.send = (name, data) => {
148
+ send(name, data);
149
149
  return promise;
150
150
  };
151
151
 
152
- promise.exit = function () {
153
- send(ps, 'init-exit');
154
-
152
+ promise.exit = () => {
153
+ send('init-exit');
155
154
  return promise;
156
155
  };
157
156
 
158
- // send 'run' event only when fork is listening for it
159
- var isReady = false;
157
+ // Send 'run' event only when fork is listening for it
158
+ let isReady = false;
160
159
 
161
- ps.on('stats', function () {
160
+ ps.on('stats', () => {
162
161
  isReady = true;
163
162
  });
164
163
 
165
- promise.run = function (options) {
164
+ promise.run = options => {
166
165
  if (isReady) {
167
- send(ps, 'run', options);
166
+ send('run', options);
168
167
  return promise;
169
168
  }
170
169
 
171
- ps.on('stats', function () {
172
- send(ps, 'run', options);
170
+ ps.on('stats', () => {
171
+ send('run', options);
173
172
  });
174
173
 
175
174
  return promise;
@@ -0,0 +1,72 @@
1
+ 'use strict';
2
+ const indentString = require('indent-string');
3
+ const chalk = require('chalk');
4
+ const diff = require('diff');
5
+
6
+ const cleanUp = line => {
7
+ if (line[0] === '+') {
8
+ return `${chalk.green('+')} ${line.slice(1)}`;
9
+ }
10
+
11
+ if (line[0] === '-') {
12
+ return `${chalk.red('-')} ${line.slice(1)}`;
13
+ }
14
+
15
+ if (line.match(/@@/)) {
16
+ return null;
17
+ }
18
+
19
+ if (line.match(/\\ No newline/)) {
20
+ return null;
21
+ }
22
+
23
+ return ` ${line}`;
24
+ };
25
+
26
+ module.exports = err => {
27
+ if (err.statements) {
28
+ const statements = JSON.parse(err.statements);
29
+
30
+ return statements
31
+ .map(statement => `${statement[0]}\n${chalk.grey('=>')} ${statement[1]}`)
32
+ .join('\n\n') + '\n';
33
+ }
34
+
35
+ if ((err.actualType === 'object' || err.actualType === 'array') && err.actualType === err.expectedType) {
36
+ const patch = diff.createPatch('string', err.actual, err.expected);
37
+ const msg = patch
38
+ .split('\n')
39
+ .slice(4)
40
+ .map(cleanUp)
41
+ .filter(Boolean)
42
+ .join('\n');
43
+
44
+ return `Difference:\n\n${msg}`;
45
+ }
46
+
47
+ if (err.actualType === 'string' && err.expectedType === 'string') {
48
+ const patch = diff.diffChars(err.actual, err.expected);
49
+ const msg = patch
50
+ .map(part => {
51
+ if (part.added) {
52
+ return chalk.bgGreen.black(part.value);
53
+ }
54
+
55
+ if (part.removed) {
56
+ return chalk.bgRed.black(part.value);
57
+ }
58
+
59
+ return part.value;
60
+ })
61
+ .join('');
62
+
63
+ return `Difference:\n\n${msg}\n`;
64
+ }
65
+
66
+ return [
67
+ 'Actual:\n',
68
+ `${indentString(err.actual, 2)}\n`,
69
+ 'Expected:\n',
70
+ `${indentString(err.expected, 2)}\n`
71
+ ].join('\n');
72
+ };
package/lib/globals.js CHANGED
@@ -1,15 +1,10 @@
1
1
  'use strict';
2
2
 
3
- // Global objects / functions to be bound before requiring test file, so tests do not interfere.
4
-
5
- var x = module.exports;
3
+ // Global objects / functions to be bound before requiring test file, so tests do not interfere
6
4
 
5
+ const x = module.exports;
7
6
  x.now = Date.now;
8
-
9
7
  x.setTimeout = setTimeout;
10
-
11
8
  x.clearTimeout = clearTimeout;
12
-
13
- x.setImmediate = require('set-immediate-shim');
14
-
9
+ x.setImmediate = setImmediate;
15
10
  x.options = {};
package/lib/hook.js CHANGED
@@ -1,27 +1,22 @@
1
1
  'use strict';
2
- var Test = require('./test');
2
+ const Test = require('./test');
3
3
 
4
- module.exports = Hook;
4
+ class Hook {
5
+ constructor(title, fn) {
6
+ if (typeof title === 'function') {
7
+ fn = title;
8
+ title = null;
9
+ }
5
10
 
6
- function Hook(title, fn) {
7
- if (!(this instanceof Hook)) {
8
- throw new TypeError('Class constructor Hook cannot be invoked without \'new\'');
11
+ this.title = title;
12
+ this.fn = fn;
9
13
  }
10
-
11
- if (typeof title === 'function') {
12
- fn = title;
13
- title = null;
14
+ test(testTitle) {
15
+ const title = this.title || `${this.metadata.type} for "${testTitle}"`;
16
+ const test = new Test(title, this.fn);
17
+ test.metadata = this.metadata;
18
+ return test;
14
19
  }
15
-
16
- this.title = title;
17
- this.fn = fn;
18
20
  }
19
21
 
20
- Hook.prototype.test = function (testTitle) {
21
- var title = this.title || (this.metadata.type + ' for "' + testTitle + '"');
22
- var test = new Test(title, this.fn);
23
-
24
- test.metadata = this.metadata;
25
-
26
- return test;
27
- };
22
+ module.exports = Hook;