ava 0.18.1 → 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/api.js CHANGED
@@ -49,12 +49,7 @@ class Api extends EventEmitter {
49
49
  super();
50
50
  autoBind(this);
51
51
 
52
- this.options = Object.assign({
53
- cwd: process.cwd(),
54
- resolveTestsFrom: process.cwd(),
55
- match: []
56
- }, options);
57
-
52
+ this.options = Object.assign({match: []}, options);
58
53
  this.options.require = resolveModules(this.options.require);
59
54
  }
60
55
  _runFile(file, runStatus, execArgv) {
@@ -119,6 +114,11 @@ class Api extends EventEmitter {
119
114
  _precompileHelpers() {
120
115
  this._precompiledHelpers = {};
121
116
 
117
+ // Assumes the tests only load helpers from within the `resolveTestsFrom`
118
+ // directory. Without arguments this is the `projectDir`, else it's
119
+ // `process.cwd()` which may be nested too deeply. This will be solved
120
+ // as we implement RFC 001 and move helper compilation into the worker
121
+ // processes, avoiding the need for precompilation.
122
122
  return new AvaFiles({cwd: this.options.resolveTestsFrom})
123
123
  .findTestHelpers()
124
124
  .map(file => { // eslint-disable-line array-callback-return
package/index.js.flow CHANGED
@@ -31,7 +31,7 @@ type ErrorValidator =
31
31
  | ((error: any) => boolean);
32
32
 
33
33
  /**
34
- * Asertion Types
34
+ * Assertion Types
35
35
  */
36
36
 
37
37
  type AssertContext = {
@@ -57,13 +57,19 @@ type AssertContext = {
57
57
  notDeepEqual<U>(value: U, expected: U, message?: string): void;
58
58
  // Assert that function throws an error or promise rejects.
59
59
  // @param error Can be a constructor, regex, error message or validation function.
60
- throws(value: PromiseLike<mixed>, error?: ErrorValidator, message?: string): Promise<mixed>;
61
- throws(value: () => void, error?: ErrorValidator, message?: string): mixed;
60
+ throws: {
61
+ (value: PromiseLike<mixed>, error?: ErrorValidator, message?: string): Promise<Error>;
62
+ (value: () => mixed, error?: ErrorValidator, message?: string): Error;
63
+ };
62
64
  // Assert that function doesn't throw an error or promise resolves.
63
- notThrows<U>(value: PromiseLike<U>, message?: string): Promise<U>;
64
- notThrows(value: () => void, message?: string): void;
65
+ notThrows: {
66
+ <U>(value: PromiseLike<U>, message?: string): Promise<U>;
67
+ (value: () => mixed, message?: string): void;
68
+ };
65
69
  // Assert that contents matches regex.
66
70
  regex(contents: string, regex: RegExp, message?: string): void;
71
+ // Assert that contents matches a snapshot.
72
+ snapshot(contents: any, message?: string): void;
67
73
  // Assert that contents does not match regex.
68
74
  notRegex(contents: string, regex: RegExp, message?: string): void;
69
75
  // Assert that error is falsy.
package/lib/cli.js CHANGED
@@ -24,7 +24,7 @@ exports.run = () => {
24
24
  const conf = pkgConf.sync('ava');
25
25
 
26
26
  const filepath = pkgConf.filepath(conf);
27
- const pkgDir = filepath === null ? process.cwd() : path.dirname(filepath);
27
+ const projectDir = filepath === null ? process.cwd() : path.dirname(filepath);
28
28
 
29
29
  const cli = meow(`
30
30
  Usage
@@ -38,6 +38,7 @@ exports.run = () => {
38
38
  --verbose, -v Enable verbose output
39
39
  --no-cache Disable the transpiler cache
40
40
  --no-power-assert Disable Power Assert
41
+ --no-color Disable color output
41
42
  --match, -m Only run tests with matching title (Can be repeated)
42
43
  --watch, -w Re-run tests when tests and source files change
43
44
  --timeout, -T Set global timeout
@@ -68,9 +69,10 @@ exports.run = () => {
68
69
  'tap',
69
70
  'verbose',
70
71
  'watch',
71
- 'update-snapshots'
72
+ 'update-snapshots',
73
+ 'color'
72
74
  ],
73
- default: conf,
75
+ default: Object.assign({color: true}, conf),
74
76
  alias: {
75
77
  t: 'tap',
76
78
  v: 'verbose',
@@ -114,11 +116,12 @@ exports.run = () => {
114
116
  explicitTitles: cli.flags.watch,
115
117
  match: arrify(cli.flags.match),
116
118
  babelConfig: babelConfig.validate(conf.babel),
117
- resolveTestsFrom: cli.input.length === 0 ? pkgDir : process.cwd(),
118
- pkgDir,
119
+ resolveTestsFrom: cli.input.length === 0 ? projectDir : process.cwd(),
120
+ projectDir,
119
121
  timeout: cli.flags.timeout,
120
122
  concurrency: cli.flags.concurrency ? parseInt(cli.flags.concurrency, 10) : 0,
121
- updateSnapshots: cli.flags.updateSnapshots
123
+ updateSnapshots: cli.flags.updateSnapshots,
124
+ color: cli.flags.color
122
125
  });
123
126
 
124
127
  let reporter;
@@ -126,9 +129,9 @@ exports.run = () => {
126
129
  if (cli.flags.tap && !cli.flags.watch) {
127
130
  reporter = new TapReporter();
128
131
  } else if (cli.flags.verbose || isCi) {
129
- reporter = new VerboseReporter({basePath: pkgDir});
132
+ reporter = new VerboseReporter({color: cli.flags.color});
130
133
  } else {
131
- reporter = new MiniReporter({watching: cli.flags.watch, basePath: pkgDir});
134
+ reporter = new MiniReporter({color: cli.flags.color, watching: cli.flags.watch});
132
135
  }
133
136
 
134
137
  reporter.api = api;
@@ -8,12 +8,25 @@ const chalk = require('chalk');
8
8
  const formatLineNumber = (lineNumber, maxLineNumber) =>
9
9
  ' '.repeat(Math.max(0, String(maxLineNumber).length - String(lineNumber).length)) + lineNumber;
10
10
 
11
- module.exports = (file, line, options) => {
12
- options = options || {};
11
+ module.exports = (source, options) => {
12
+ if (!source.isWithinProject || source.isDependency) {
13
+ return null;
14
+ }
13
15
 
16
+ const file = source.file;
17
+ const line = source.line;
18
+
19
+ options = options || {};
14
20
  const maxWidth = options.maxWidth || 80;
15
- const source = fs.readFileSync(file, 'utf8');
16
- const excerpt = codeExcerpt(source, line, {around: 1});
21
+
22
+ let contents;
23
+ try {
24
+ contents = fs.readFileSync(file, 'utf8');
25
+ } catch (err) {
26
+ return null;
27
+ }
28
+
29
+ const excerpt = codeExcerpt(contents, line, {around: 1});
17
30
  if (!excerpt) {
18
31
  return null;
19
32
  }
package/lib/fork.js CHANGED
@@ -36,8 +36,10 @@ module.exports = (file, opts, execArgv) => {
36
36
  } : false
37
37
  }, opts);
38
38
 
39
- const ps = childProcess.fork(path.join(__dirname, 'test-worker.js'), [JSON.stringify(opts)], {
40
- cwd: opts.pkgDir,
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,
41
43
  silent: true,
42
44
  env,
43
45
  execArgv: execArgv || process.execArgv
package/lib/main.js CHANGED
@@ -70,7 +70,11 @@ globals.setImmediate(() => {
70
70
  runner.on('test', test);
71
71
 
72
72
  process.on('ava-run', options => {
73
- runner.run(options).then(exit);
73
+ runner.run(options)
74
+ .then(exit)
75
+ .catch(err => {
76
+ process.emit('uncaughtException', err);
77
+ });
74
78
  });
75
79
 
76
80
  process.on('ava-init-exit', () => {
@@ -1,6 +1,5 @@
1
1
  'use strict';
2
2
  const StringDecoder = require('string_decoder').StringDecoder;
3
- const path = require('path');
4
3
  const cliCursor = require('cli-cursor');
5
4
  const lastLineTracker = require('last-line-stream/tracker');
6
5
  const plur = require('plur');
@@ -14,11 +13,6 @@ const extractStack = require('../extract-stack');
14
13
  const codeExcerpt = require('../code-excerpt');
15
14
  const colors = require('../colors');
16
15
 
17
- chalk.enabled = true;
18
- Object.keys(colors).forEach(key => {
19
- colors[key].enabled = true;
20
- });
21
-
22
16
  // TODO(@jamestalamge): This should be fixed in log-update and ansi-escapes once we are confident it's a good solution.
23
17
  const CSI = '\u001b[';
24
18
  const ERASE_LINE = CSI + '2K';
@@ -42,12 +36,17 @@ function eraseLines(count) {
42
36
 
43
37
  class MiniReporter {
44
38
  constructor(options) {
39
+ this.options = Object.assign({}, options);
40
+
41
+ chalk.enabled = this.options.color;
42
+ for (const key of Object.keys(colors)) {
43
+ colors[key].enabled = this.options.color;
44
+ }
45
+
45
46
  const spinnerDef = spinners[process.platform === 'win32' ? 'line' : 'dots'];
46
47
  this.spinnerFrames = spinnerDef.frames.map(c => chalk.gray.dim(c));
47
48
  this.spinnerInterval = spinnerDef.interval;
48
49
 
49
- this.options = Object.assign({}, options);
50
-
51
50
  this.reset();
52
51
  this.stream = process.stderr;
53
52
  this.stringDecoder = new StringDecoder();
@@ -187,8 +186,7 @@ class MiniReporter {
187
186
  if (test.error.source) {
188
187
  status += ' ' + colors.errorSource(test.error.source.file + ':' + test.error.source.line) + '\n';
189
188
 
190
- const errorPath = path.join(this.options.basePath, test.error.source.file);
191
- const excerpt = codeExcerpt(errorPath, test.error.source.line, {maxWidth: process.stdout.columns});
189
+ const excerpt = codeExcerpt(test.error.source, {maxWidth: process.stdout.columns});
192
190
  if (excerpt) {
193
191
  status += '\n' + indentString(excerpt, 2) + '\n';
194
192
  }
@@ -204,7 +202,10 @@ class MiniReporter {
204
202
  }
205
203
 
206
204
  if (test.error.stack) {
207
- status += '\n' + indentString(colors.errorStack(extractStack(test.error.stack)), 2);
205
+ const extracted = extractStack(test.error.stack);
206
+ if (extracted.includes('\n')) {
207
+ status += '\n' + indentString(colors.errorStack(extracted), 2);
208
+ }
208
209
  }
209
210
  });
210
211
  }
@@ -233,7 +234,8 @@ class MiniReporter {
233
234
  }
234
235
 
235
236
  if (runStatus.failFastEnabled === true && runStatus.remainingCount > 0 && runStatus.failCount > 0) {
236
- status += '\n\n ' + colors.information('`--fail-fast` is on. Any number of tests may have been skipped');
237
+ const remaining = 'At least ' + runStatus.remainingCount + ' ' + plur('test was', 'tests were', runStatus.remainingCount) + ' skipped.';
238
+ status += '\n\n ' + colors.information('`--fail-fast` is on. ' + remaining);
237
239
  }
238
240
 
239
241
  if (runStatus.hasExclusive === true && runStatus.remainingCount > 0) {
@@ -1,5 +1,4 @@
1
1
  'use strict';
2
- const path = require('path');
3
2
  const indentString = require('indent-string');
4
3
  const prettyMs = require('pretty-ms');
5
4
  const figures = require('figures');
@@ -10,13 +9,14 @@ const extractStack = require('../extract-stack');
10
9
  const codeExcerpt = require('../code-excerpt');
11
10
  const colors = require('../colors');
12
11
 
13
- Object.keys(colors).forEach(key => {
14
- colors[key].enabled = true;
15
- });
16
-
17
12
  class VerboseReporter {
18
13
  constructor(options) {
19
14
  this.options = Object.assign({}, options);
15
+
16
+ chalk.enabled = this.options.color;
17
+ for (const key of Object.keys(colors)) {
18
+ colors[key].enabled = this.options.color;
19
+ }
20
20
  }
21
21
  start() {
22
22
  return '';
@@ -105,8 +105,7 @@ class VerboseReporter {
105
105
  if (test.error.source) {
106
106
  output += ' ' + colors.errorSource(test.error.source.file + ':' + test.error.source.line) + '\n';
107
107
 
108
- const errorPath = path.join(this.options.basePath, test.error.source.file);
109
- const excerpt = codeExcerpt(errorPath, test.error.source.line, {maxWidth: process.stdout.columns});
108
+ const excerpt = codeExcerpt(test.error.source, {maxWidth: process.stdout.columns});
110
109
  if (excerpt) {
111
110
  output += '\n' + indentString(excerpt, 2) + '\n';
112
111
  }
@@ -122,13 +121,17 @@ class VerboseReporter {
122
121
  }
123
122
 
124
123
  if (test.error.stack) {
125
- output += '\n' + indentString(colors.errorStack(extractStack(test.error.stack)), 2);
124
+ const extracted = extractStack(test.error.stack);
125
+ if (extracted.includes('\n')) {
126
+ output += '\n' + indentString(colors.errorStack(extracted), 2);
127
+ }
126
128
  }
127
129
  });
128
130
  }
129
131
 
130
132
  if (runStatus.failFastEnabled === true && runStatus.remainingCount > 0 && runStatus.failCount > 0) {
131
- output += '\n\n\n ' + colors.information('`--fail-fast` is on. Any number of tests may have been skipped');
133
+ const remaining = 'At least ' + runStatus.remainingCount + ' ' + plur('test was', 'tests were', runStatus.remainingCount) + ' skipped.';
134
+ output += '\n\n\n ' + colors.information('`--fail-fast` is on. ' + remaining);
132
135
  }
133
136
 
134
137
  if (runStatus.hasExclusive === true && runStatus.remainingCount > 0) {
@@ -1,4 +1,5 @@
1
1
  'use strict';
2
+ const path = require('path');
2
3
  const cleanYamlObject = require('clean-yaml-object');
3
4
  const StackUtils = require('stack-utils');
4
5
  const prettyFormat = require('@ava/pretty-format');
@@ -8,6 +9,7 @@ const extractStack = require('./extract-stack');
8
9
 
9
10
  function serializeValue(value) {
10
11
  return prettyFormat(value, {
12
+ callToJSON: false,
11
13
  plugins: [reactTestPlugin],
12
14
  highlight: true
13
15
  });
@@ -58,8 +60,21 @@ module.exports = error => {
58
60
  const firstStackLine = extractStack(err.stack).split('\n')[0];
59
61
  const source = stackUtils.parseLine(firstStackLine);
60
62
  if (source) {
63
+ // Assume the CWD is the project directory. This holds since this function
64
+ // is only called in test workers, which are created with their working
65
+ // directory set to the project directory.
66
+ const projectDir = process.cwd();
67
+
68
+ const file = path.resolve(projectDir, source.file.trim());
69
+ const rel = path.relative(projectDir, file);
70
+
71
+ const isWithinProject = rel.split(path.sep)[0] !== '..';
72
+ const isDependency = isWithinProject && path.dirname(rel).split(path.sep).indexOf('node_modules') > -1;
73
+
61
74
  err.source = {
62
- file: source.file.trim(),
75
+ isDependency,
76
+ isWithinProject,
77
+ file,
63
78
  line: source.line
64
79
  };
65
80
  }
@@ -36,7 +36,20 @@ process.on('unhandledRejection', throwsHelper);
36
36
 
37
37
  process.on('uncaughtException', exception => {
38
38
  throwsHelper(exception);
39
- send('uncaughtException', {exception: serializeError(exception)});
39
+
40
+ let serialized;
41
+ try {
42
+ serialized = serializeError(exception);
43
+ } catch (ignore) { // eslint-disable-line unicorn/catch-error-name
44
+ // Avoid using serializeError
45
+ const err = new Error('Failed to serialize uncaught exception');
46
+ serialized = {
47
+ name: err.name,
48
+ message: err.message,
49
+ stack: err.stack
50
+ };
51
+ }
52
+ send('uncaughtException', {exception: serialized});
40
53
  });
41
54
 
42
55
  // If AVA was not required, show an error
package/lib/test.js CHANGED
@@ -240,10 +240,9 @@ class Test {
240
240
  this._setAssertError(new Error(`Do not return ${asyncType} from tests declared via \`test.cb(...)\`, if you want to return a promise simply declare the test via \`test(...)\``));
241
241
  }
242
242
 
243
- ret.then(
244
- () => {
245
- this.exit();
246
- },
243
+ // Convert to a Bluebird promise
244
+ return Promise.resolve(ret).then(
245
+ () => this.exit(),
247
246
  err => {
248
247
  if (!(err instanceof Error)) {
249
248
  err = new assert.AssertionError({
@@ -254,10 +253,9 @@ class Test {
254
253
  }
255
254
 
256
255
  this._setAssertError(err);
257
- this.exit();
258
- });
259
-
260
- return this.promise().promise;
256
+ return this.exit();
257
+ }
258
+ );
261
259
  }
262
260
 
263
261
  if (this.metadata.callback && !this.threwSync) {
package/lib/watcher.js CHANGED
@@ -107,13 +107,15 @@ class Watcher {
107
107
  }
108
108
  }
109
109
 
110
- this.busy = api.run(specificFiles || files, {runOnlyExclusive}).then(runStatus => {
111
- runStatus.previousFailCount = this.sumPreviousFailures(currentVector);
112
- logger.finish(runStatus);
113
-
114
- const badCounts = runStatus.failCount + runStatus.rejectionCount + runStatus.exceptionCount;
115
- this.clearLogOnNextRun = this.clearLogOnNextRun && badCounts === 0;
116
- }, rethrowAsync);
110
+ this.busy = api.run(specificFiles || files, {runOnlyExclusive})
111
+ .then(runStatus => {
112
+ runStatus.previousFailCount = this.sumPreviousFailures(currentVector);
113
+ logger.finish(runStatus);
114
+
115
+ const badCounts = runStatus.failCount + runStatus.rejectionCount + runStatus.exceptionCount;
116
+ this.clearLogOnNextRun = this.clearLogOnNextRun && badCounts === 0;
117
+ })
118
+ .catch(rethrowAsync);
117
119
  };
118
120
 
119
121
  this.testDependencies = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ava",
3
- "version": "0.18.1",
3
+ "version": "0.18.2",
4
4
  "description": "Futuristic test runner 🚀",
5
5
  "license": "MIT",
6
6
  "repository": "avajs/ava",
@@ -43,7 +43,7 @@
43
43
  "node": ">=4"
44
44
  },
45
45
  "scripts": {
46
- "test": "xo && nyc tap --no-cov --timeout=150 --jobs=4 test/*.js test/reporters/*.js",
46
+ "test": "xo && flow check test/flow-types && nyc tap --no-cov --timeout=150 --jobs=4 test/*.js test/reporters/*.js",
47
47
  "test-win": "tap --no-cov --reporter=classic --timeout=150 --jobs=4 test/*.js test/reporters/*.js",
48
48
  "visual": "node test/visual/run-visual-tests.js",
49
49
  "prepublish": "npm run make-ts",
@@ -89,7 +89,7 @@
89
89
  "dependencies": {
90
90
  "@ava/babel-preset-stage-4": "^1.0.0",
91
91
  "@ava/babel-preset-transform-test-files": "^2.0.0",
92
- "@ava/pretty-format": "^1.0.0",
92
+ "@ava/pretty-format": "^1.1.0",
93
93
  "arr-flatten": "^1.0.1",
94
94
  "array-union": "^1.0.1",
95
95
  "array-uniq": "^1.0.2",
@@ -154,7 +154,7 @@
154
154
  "resolve-cwd": "^1.0.0",
155
155
  "slash": "^1.0.0",
156
156
  "source-map-support": "^0.4.0",
157
- "stack-utils": "^0.4.0",
157
+ "stack-utils": "^1.0.0",
158
158
  "strip-ansi": "^3.0.1",
159
159
  "strip-bom-buf": "^1.0.0",
160
160
  "time-require": "^0.1.2",
@@ -167,6 +167,7 @@
167
167
  "coveralls": "^2.11.4",
168
168
  "delay": "^1.3.0",
169
169
  "execa": "^0.6.0",
170
+ "flow-bin": "^0.38.0",
170
171
  "get-stream": "^3.0.0",
171
172
  "git-branch": "^0.3.0",
172
173
  "has-ansi": "^2.0.0",
@@ -182,7 +183,7 @@
182
183
  "sinon": "^1.17.2",
183
184
  "source-map-fixtures": "^2.1.0",
184
185
  "tap": "^10.0.0",
185
- "temp-write": "^3.0.0",
186
+ "temp-write": "^3.1.0",
186
187
  "touch": "^1.0.0",
187
188
  "xo": "^0.17.0",
188
189
  "zen-observable": "^0.4.0"
@@ -196,6 +197,7 @@
196
197
  },
197
198
  "nyc": {
198
199
  "reporter": [
200
+ "html",
199
201
  "lcov",
200
202
  "text"
201
203
  ]
package/readme.md CHANGED
@@ -42,6 +42,7 @@ Translations: [Español](https://github.com/avajs/ava-docs/blob/master/es_ES/rea
42
42
  - Runs tests concurrently
43
43
  - Enforces writing atomic tests
44
44
  - No implicit globals
45
+ - Includes TypeScript & Flow type definitions
45
46
  - [Magic assert](#magic-assert)
46
47
  - [Isolated environment for each test file](#process-isolation)
47
48
  - [Write your tests in ES2017](#es2017-support)
@@ -163,6 +164,7 @@ $ ava --help
163
164
  --verbose, -v Enable verbose output
164
165
  --no-cache Disable the transpiler cache
165
166
  --no-power-assert Disable Power Assert
167
+ --no-color Disable color output
166
168
  --match, -m Only run tests with matching title (Can be repeated)
167
169
  --watch, -w Re-run tests when tests and source files change
168
170
  --timeout, -T Set global timeout
@@ -912,11 +914,15 @@ Assert that `value` is not equal to `expected`.
912
914
 
913
915
  ### `.deepEqual(value, expected, [message])`
914
916
 
915
- Assert that `value` is deep equal to `expected`.
917
+ Assert that `value` is deep equal to `expected`. This is based on [Lodash' `isEqual()`](https://lodash.com/docs/4.17.4#isEqual):
918
+
919
+ > Performs a deep comparison between two values to determine if they are equivalent.
920
+ >
921
+ > *Note*: This method supports comparing arrays, array buffers, booleans, date objects, error objects, maps, numbers, `Object` objects, regexes, sets, strings, symbols, and typed arrays. `Object` objects are compared by their own, not inherited, enumerable properties. Functions and DOM nodes are compared by strict equality, i.e. `===`.
916
922
 
917
923
  ### `.notDeepEqual(value, expected, [message])`
918
924
 
919
- Assert that `value` is not deep equal to `expected`.
925
+ Assert that `value` is not deep equal to `expected`. The inverse of `.deepEqual()`.
920
926
 
921
927
  ### `.throws(function|promise, [error, [message]])`
922
928
 
@@ -71,6 +71,10 @@ export interface AssertContext {
71
71
  * Assert that contents matches regex.
72
72
  */
73
73
  regex(contents: string, regex: RegExp, message?: string): void;
74
+ /**
75
+ * Assert that contents matches a snapshot.
76
+ */
77
+ snapshot(contents: any, message?: string): void;
74
78
  /**
75
79
  * Assert that contents does not match regex.
76
80
  */