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.
@@ -1,21 +1,20 @@
1
1
  'use strict';
2
- var path = require('path');
3
- var chalk = require('chalk');
4
- var figures = require('figures');
5
- var convertSourceMap = require('convert-source-map');
6
- var objectAssign = require('object-assign');
7
- var colors = require('./colors');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+ const figures = require('figures');
5
+ const convertSourceMap = require('convert-source-map');
6
+ const colors = require('./colors');
8
7
 
9
8
  function validate(conf) {
10
9
  if (conf === undefined || conf === null) {
11
10
  conf = 'default';
12
11
  }
13
12
 
14
- // check for valid babel config shortcuts (can be either "default" or "inherit")
15
- var isValidShortcut = conf === 'default' || conf === 'inherit';
13
+ // Check for valid babel config shortcuts (can be either `default` or `inherit`)
14
+ const isValidShortcut = conf === 'default' || conf === 'inherit';
16
15
 
17
16
  if (!conf || (typeof conf === 'string' && !isValidShortcut)) {
18
- var message = colors.error(figures.cross);
17
+ let message = colors.error(figures.cross);
19
18
  message += ' Unexpected Babel configuration for AVA. ';
20
19
  message += 'See ' + chalk.underline('https://github.com/avajs/ava#es2015-support') + ' for allowed values.';
21
20
 
@@ -25,74 +24,35 @@ function validate(conf) {
25
24
  return conf;
26
25
  }
27
26
 
28
- function lazy(initFn) {
29
- var initialized = false;
30
- var value;
27
+ function lazy(buildPreset) {
28
+ let preset;
31
29
 
32
- return function () {
33
- if (!initialized) {
34
- initialized = true;
35
- value = initFn();
30
+ return babel => {
31
+ if (!preset) {
32
+ preset = buildPreset(babel);
36
33
  }
37
34
 
38
- return value;
35
+ return preset;
39
36
  };
40
37
  }
41
38
 
42
- var defaultPresets = lazy(function () {
43
- return [
44
- require('babel-preset-stage-2'),
45
- require('babel-preset-es2015')
46
- ];
47
- });
39
+ const stage4 = lazy(() => require('@ava/babel-preset-stage-4')());
48
40
 
49
- var rewritePlugin = lazy(function () {
50
- var wrapListener = require('babel-plugin-detective/wrap-listener');
51
-
52
- return wrapListener(rewriteBabelRuntimePaths, 'rewrite-runtime', {
53
- generated: true,
54
- require: true,
55
- import: true
41
+ function makeTransformTestFiles(powerAssert) {
42
+ return lazy(babel => {
43
+ return require('@ava/babel-preset-transform-test-files')(babel, {powerAssert});
56
44
  });
57
- });
58
-
59
- function rewriteBabelRuntimePaths(path) {
60
- var isBabelPath = /^babel-runtime[\\\/]?/.test(path.node.value);
61
-
62
- if (path.isLiteral() && isBabelPath) {
63
- path.node.value = require.resolve(path.node.value);
64
- }
65
45
  }
66
46
 
67
- var espowerPlugin = lazy(function () {
68
- var babel = require('babel-core');
69
- var createEspowerPlugin = require('babel-plugin-espower/create');
70
-
71
- // initialize power-assert
72
- return createEspowerPlugin(babel, {
73
- embedAst: true,
74
- patterns: require('./enhance-assert').PATTERNS
75
- });
76
- });
77
-
78
- var defaultPlugins = lazy(function () {
79
- return [
80
- espowerPlugin(),
81
- require('babel-plugin-ava-throws-helper'),
82
- rewritePlugin(),
83
- require('babel-plugin-transform-runtime')
84
- ];
85
- });
86
-
87
- function build(babelConfig, filePath, code) {
47
+ function build(babelConfig, powerAssert, filePath, code) {
88
48
  babelConfig = validate(babelConfig);
89
49
 
90
- var options;
50
+ let options;
91
51
 
92
52
  if (babelConfig === 'default') {
93
53
  options = {
94
54
  babelrc: false,
95
- presets: defaultPresets()
55
+ presets: [stage4]
96
56
  };
97
57
  } else if (babelConfig === 'inherit') {
98
58
  options = {
@@ -103,29 +63,31 @@ function build(babelConfig, filePath, code) {
103
63
  babelrc: false
104
64
  };
105
65
 
106
- objectAssign(options, babelConfig);
66
+ Object.assign(options, babelConfig);
107
67
  }
108
68
 
109
- var sourceMap = getSourceMap(filePath, code);
69
+ const sourceMap = getSourceMap(filePath, code);
110
70
 
111
- objectAssign(options, {
71
+ Object.assign(options, {
112
72
  inputSourceMap: sourceMap,
113
73
  filename: filePath,
114
74
  sourceMaps: true,
115
75
  ast: false
116
76
  });
117
77
 
118
- options.plugins = (options.plugins || []).concat(defaultPlugins());
78
+ if (!options.presets) {
79
+ options.presets = [];
80
+ }
81
+ options.presets.push(makeTransformTestFiles(powerAssert));
119
82
 
120
83
  return options;
121
84
  }
122
85
 
123
86
  function getSourceMap(filePath, code) {
124
- var sourceMap = convertSourceMap.fromSource(code);
87
+ let sourceMap = convertSourceMap.fromSource(code);
125
88
 
126
89
  if (!sourceMap) {
127
- var dirPath = path.dirname(filePath);
128
-
90
+ const dirPath = path.dirname(filePath);
129
91
  sourceMap = convertSourceMap.fromMapFileSource(code, dirPath);
130
92
  }
131
93
 
@@ -137,10 +99,10 @@ function getSourceMap(filePath, code) {
137
99
  }
138
100
 
139
101
  module.exports = {
140
- validate: validate,
141
- build: build,
142
- pluginPackages: [
143
- require.resolve('babel-core/package.json'),
144
- require.resolve('babel-plugin-espower/package.json')
102
+ validate,
103
+ build,
104
+ presetHashes: [
105
+ require('@ava/babel-preset-stage-4/package-hash'),
106
+ require('@ava/babel-preset-transform-test-files/package-hash')
145
107
  ]
146
108
  };
@@ -1,16 +1,13 @@
1
1
  'use strict';
2
- var StackUtils = require('stack-utils');
3
- var debug = require('debug')('ava');
2
+ const StackUtils = require('stack-utils');
3
+ const cleanStack = require('clean-stack');
4
+ const debug = require('debug')('ava');
4
5
 
5
- function indent(str) {
6
- return ' ' + str;
7
- }
8
-
9
- // ignore unimportant stack trace lines
10
- var ignoreStackLines = [];
6
+ // Ignore unimportant stack trace lines
7
+ let ignoreStackLines = [];
11
8
 
12
- var avaInternals = /\/ava\/(?:lib\/)?[\w-]+\.js:\d+:\d+\)?$/;
13
- var avaDependencies = /\/node_modules\/(?:bluebird|empower-core|(?:ava\/node_modules\/)?(?:babel-runtime|core-js))\//;
9
+ const avaInternals = /\/ava\/(?:lib\/)?[\w-]+\.js:\d+:\d+\)?$/;
10
+ const avaDependencies = /\/node_modules\/(?:bluebird|empower-core|(?:ava\/node_modules\/)?(?:babel-runtime|core-js))\//;
14
11
 
15
12
  if (!debug.enabled) {
16
13
  ignoreStackLines = StackUtils.nodeInternals();
@@ -18,19 +15,23 @@ if (!debug.enabled) {
18
15
  ignoreStackLines.push(avaDependencies);
19
16
  }
20
17
 
21
- var stackUtils = new StackUtils({internals: ignoreStackLines});
18
+ const stackUtils = new StackUtils({internals: ignoreStackLines});
22
19
 
23
- module.exports = function (stack) {
20
+ module.exports = stack => {
24
21
  if (!stack) {
25
22
  return '';
26
23
  }
27
24
 
28
- var title = stack.split('\n')[0];
29
- var lines = stackUtils
25
+ // Workaround for https://github.com/tapjs/stack-utils/issues/14
26
+ // TODO: fix it in `stack-utils`
27
+ stack = cleanStack(stack);
28
+
29
+ const title = stack.split('\n')[0];
30
+ const lines = stackUtils
30
31
  .clean(stack)
31
32
  .split('\n')
32
- .map(indent)
33
+ .map(x => ` ${x}`)
33
34
  .join('\n');
34
35
 
35
- return title + '\n' + lines;
36
+ return `${title}\n${lines}`;
36
37
  };
@@ -1,96 +1,81 @@
1
1
  'use strict';
2
- var path = require('path');
3
- var fs = require('fs');
4
- var convertSourceMap = require('convert-source-map');
5
- var cachingTransform = require('caching-transform');
6
- var stripBom = require('strip-bom');
7
- var md5Hex = require('md5-hex');
8
- var packageHash = require('package-hash');
9
- var babelConfigHelper = require('./babel-config');
10
-
11
- function CachingPrecompiler(cacheDirPath, babelConfig) {
12
- if (!(this instanceof CachingPrecompiler)) {
13
- throw new TypeError('Class constructor CachingPrecompiler cannot be invoked without \'new\'');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+ const convertSourceMap = require('convert-source-map');
5
+ const cachingTransform = require('caching-transform');
6
+ const packageHash = require('package-hash');
7
+ const stripBomBuf = require('strip-bom-buf');
8
+ const autoBind = require('auto-bind');
9
+ const md5Hex = require('md5-hex');
10
+ const babelConfigHelper = require('./babel-config');
11
+
12
+ class CachingPrecompiler {
13
+ constructor(options) {
14
+ autoBind(this);
15
+
16
+ options = options || {};
17
+
18
+ this.babelConfig = babelConfigHelper.validate(options.babel);
19
+ this.cacheDirPath = options.path;
20
+ this.powerAssert = Boolean(options.powerAssert);
21
+ this.fileHashes = {};
22
+ this.transform = this._createTransform();
14
23
  }
24
+ precompileFile(filePath) {
25
+ if (!this.fileHashes[filePath]) {
26
+ const source = stripBomBuf(fs.readFileSync(filePath));
27
+ this.transform(source, filePath);
28
+ }
15
29
 
16
- this.babelConfig = babelConfigHelper.validate(babelConfig);
17
- this.cacheDirPath = cacheDirPath;
18
- this.fileHashes = {};
19
-
20
- Object.keys(CachingPrecompiler.prototype).forEach(function (name) {
21
- this[name] = this[name].bind(this);
22
- }, this);
23
-
24
- this.transform = this._createTransform();
25
- }
26
-
27
- module.exports = CachingPrecompiler;
28
-
29
- CachingPrecompiler.prototype.precompileFile = function (filePath) {
30
- if (!this.fileHashes[filePath]) {
31
- var source = stripBom(fs.readFileSync(filePath));
32
-
33
- this.transform(source, filePath);
30
+ return this.fileHashes[filePath];
34
31
  }
32
+ // Conditionally called by caching-transform when precompiling is required
33
+ _init() {
34
+ this.babel = require('babel-core');
35
+ return this._transform;
36
+ }
37
+ _transform(code, filePath, hash) {
38
+ code = code.toString();
35
39
 
36
- return this.fileHashes[filePath];
37
- };
38
-
39
- // conditionally called by caching-transform when precompiling is required
40
- CachingPrecompiler.prototype._factory = function () {
41
- this._init();
42
-
43
- return this._transform;
44
- };
45
-
46
- CachingPrecompiler.prototype._init = function () {
47
- this.babel = require('babel-core');
48
- };
49
-
50
- CachingPrecompiler.prototype._transform = function (code, filePath, hash) {
51
- code = code.toString();
52
-
53
- var options = babelConfigHelper.build(this.babelConfig, filePath, code);
54
- var result = this.babel.transform(code, options);
55
-
56
- // save source map
57
- var mapPath = path.join(this.cacheDirPath, hash + '.js.map');
58
- fs.writeFileSync(mapPath, JSON.stringify(result.map));
59
-
60
- // When loading the test file, test workers intercept the require call and
61
- // load the cached code instead. Libraries like nyc may also be intercepting
62
- // require calls, however they won't know that different code was loaded.
63
- // They may then attempt to resolve a source map from the original file
64
- // location.
65
- //
66
- // Add a source map file comment to the cached code. The file path is
67
- // relative from the directory of the original file to where the source map
68
- // is cached. This will allow the source map to be resolved.
69
- var dirPath = path.dirname(filePath);
70
- var relativeMapPath = path.relative(dirPath, mapPath);
71
- var comment = convertSourceMap.generateMapFileComment(relativeMapPath);
72
-
73
- return result.code + '\n' + comment;
74
- };
40
+ const options = babelConfigHelper.build(this.babelConfig, this.powerAssert, filePath, code);
41
+ const result = this.babel.transform(code, options);
75
42
 
76
- CachingPrecompiler.prototype._createTransform = function () {
77
- var salt = packageHash.sync(
78
- [require.resolve('../package.json')].concat(babelConfigHelper.pluginPackages),
79
- JSON.stringify(this.babelConfig)
80
- );
43
+ // Save source map
44
+ const mapPath = path.join(this.cacheDirPath, `${hash}.js.map`);
45
+ fs.writeFileSync(mapPath, JSON.stringify(result.map));
81
46
 
82
- return cachingTransform({
83
- factory: this._factory,
84
- cacheDir: this.cacheDirPath,
85
- hash: this._generateHash,
86
- salt: salt,
87
- ext: '.js'
88
- });
89
- };
47
+ // Append source map comment to transformed code
48
+ // So that other libraries (like nyc) can find the source map
49
+ const dirPath = path.dirname(filePath);
50
+ const relativeMapPath = path.relative(dirPath, mapPath);
51
+ const comment = convertSourceMap.generateMapFileComment(relativeMapPath);
90
52
 
91
- CachingPrecompiler.prototype._generateHash = function (code, filePath, salt) {
92
- var hash = md5Hex([code, filePath, salt]);
93
- this.fileHashes[filePath] = hash;
53
+ return `${result.code}\n${comment}`;
54
+ }
55
+ _createTransform() {
56
+ const salt = packageHash.sync([
57
+ require.resolve('../package.json'),
58
+ require.resolve('babel-core/package.json')
59
+ ], {
60
+ babelConfig: this.babelConfig,
61
+ majorNodeVersion: process.version.split('.')[0],
62
+ powerAssert: this.powerAssert,
63
+ presetHashes: babelConfigHelper.presetHashes
64
+ });
65
+
66
+ return cachingTransform({
67
+ factory: this._init,
68
+ cacheDir: this.cacheDirPath,
69
+ hash: this._generateHash,
70
+ salt,
71
+ ext: '.js'
72
+ });
73
+ }
74
+ _generateHash(code, filePath, salt) {
75
+ const hash = md5Hex([code, filePath, salt]);
76
+ this.fileHashes[filePath] = hash;
77
+ return hash;
78
+ }
79
+ }
94
80
 
95
- return hash;
96
- };
81
+ module.exports = CachingPrecompiler;
package/lib/cli.js ADDED
@@ -0,0 +1,181 @@
1
+ 'use strict';
2
+ const path = require('path');
3
+ const updateNotifier = require('update-notifier');
4
+ const figures = require('figures');
5
+ const arrify = require('arrify');
6
+ const meow = require('meow');
7
+ const Promise = require('bluebird');
8
+ const pkgConf = require('pkg-conf');
9
+ const isCi = require('is-ci');
10
+ const hasFlag = require('has-flag');
11
+ const Api = require('../api');
12
+ const colors = require('./colors');
13
+ const VerboseReporter = require('./reporters/verbose');
14
+ const MiniReporter = require('./reporters/mini');
15
+ const TapReporter = require('./reporters/tap');
16
+ const Logger = require('./logger');
17
+ const Watcher = require('./watcher');
18
+ const babelConfig = require('./babel-config');
19
+
20
+ // Bluebird specific
21
+ Promise.longStackTraces();
22
+
23
+ exports.run = () => {
24
+ const conf = pkgConf.sync('ava');
25
+
26
+ const filepath = pkgConf.filepath(conf);
27
+ const projectDir = filepath === null ? process.cwd() : path.dirname(filepath);
28
+
29
+ const cli = meow(`
30
+ Usage
31
+ ava [<file|directory|glob> ...]
32
+
33
+ Options
34
+ --init Add AVA to your project
35
+ --fail-fast Stop after first test failure
36
+ --serial, -s Run tests serially
37
+ --tap, -t Generate TAP output
38
+ --verbose, -v Enable verbose output
39
+ --no-cache Disable the transpiler cache
40
+ --no-power-assert Disable Power Assert
41
+ --no-color Disable color output
42
+ --match, -m Only run tests with matching title (Can be repeated)
43
+ --watch, -w Re-run tests when tests and source files change
44
+ --timeout, -T Set global timeout
45
+ --concurrency, -c Maximum number of test files running at the same time (EXPERIMENTAL)
46
+ --update-snapshots, -u Update snapshots
47
+
48
+ Examples
49
+ ava
50
+ ava test.js test2.js
51
+ ava test-*.js
52
+ ava test
53
+ ava --init
54
+ ava --init foo.js
55
+
56
+ Default patterns when no arguments:
57
+ test.js test-*.js test/**/*.js **/__tests__/**/*.js **/*.test.js
58
+ `, {
59
+ string: [
60
+ '_',
61
+ 'match',
62
+ 'timeout',
63
+ 'concurrency'
64
+ ],
65
+ boolean: [
66
+ 'init',
67
+ 'fail-fast',
68
+ 'serial',
69
+ 'tap',
70
+ 'verbose',
71
+ 'watch',
72
+ 'update-snapshots',
73
+ 'color'
74
+ ],
75
+ default: Object.assign({color: true}, conf),
76
+ alias: {
77
+ t: 'tap',
78
+ v: 'verbose',
79
+ s: 'serial',
80
+ m: 'match',
81
+ w: 'watch',
82
+ T: 'timeout',
83
+ c: 'concurrency',
84
+ u: 'update-snapshots'
85
+ }
86
+ });
87
+
88
+ updateNotifier({pkg: cli.pkg}).notify();
89
+
90
+ if (cli.flags.init) {
91
+ require('ava-init')();
92
+ return;
93
+ }
94
+
95
+ if (
96
+ ((hasFlag('--watch') || hasFlag('-w')) && (hasFlag('--tap') || hasFlag('-t'))) ||
97
+ (conf.watch && conf.tap)
98
+ ) {
99
+ throw new Error(colors.error(figures.cross) + ' The TAP reporter is not available when using watch mode.');
100
+ }
101
+
102
+ if ((hasFlag('--watch') || hasFlag('-w')) && isCi) {
103
+ throw new Error(colors.error(figures.cross) + ' Watch mode is not available in CI, as it prevents AVA from terminating.');
104
+ }
105
+
106
+ if (hasFlag('--require') || hasFlag('-r')) {
107
+ throw new Error(colors.error(figures.cross) + ' The --require and -r flags are deprecated. Requirements should be configured in package.json - see documentation.');
108
+ }
109
+
110
+ const api = new Api({
111
+ failFast: cli.flags.failFast,
112
+ serial: cli.flags.serial,
113
+ require: arrify(conf.require),
114
+ cacheEnabled: cli.flags.cache !== false,
115
+ powerAssert: cli.flags.powerAssert !== false,
116
+ explicitTitles: cli.flags.watch,
117
+ match: arrify(cli.flags.match),
118
+ babelConfig: babelConfig.validate(conf.babel),
119
+ resolveTestsFrom: cli.input.length === 0 ? projectDir : process.cwd(),
120
+ projectDir,
121
+ timeout: cli.flags.timeout,
122
+ concurrency: cli.flags.concurrency ? parseInt(cli.flags.concurrency, 10) : 0,
123
+ updateSnapshots: cli.flags.updateSnapshots,
124
+ color: cli.flags.color
125
+ });
126
+
127
+ let reporter;
128
+
129
+ if (cli.flags.tap && !cli.flags.watch) {
130
+ reporter = new TapReporter();
131
+ } else if (cli.flags.verbose || isCi) {
132
+ reporter = new VerboseReporter({color: cli.flags.color});
133
+ } else {
134
+ reporter = new MiniReporter({color: cli.flags.color, watching: cli.flags.watch});
135
+ }
136
+
137
+ reporter.api = api;
138
+ const logger = new Logger(reporter);
139
+
140
+ logger.start();
141
+
142
+ api.on('test-run', runStatus => {
143
+ reporter.api = runStatus;
144
+ runStatus.on('test', logger.test);
145
+ runStatus.on('error', logger.unhandledError);
146
+
147
+ runStatus.on('stdout', logger.stdout);
148
+ runStatus.on('stderr', logger.stderr);
149
+ });
150
+
151
+ const files = cli.input.length ? cli.input : arrify(conf.files);
152
+
153
+ if (cli.flags.watch) {
154
+ try {
155
+ const watcher = new Watcher(logger, api, files, arrify(conf.source));
156
+ watcher.observeStdin(process.stdin);
157
+ } catch (err) {
158
+ if (err.name === 'AvaError') {
159
+ // An AvaError may be thrown if `chokidar` is not installed. Log it nicely.
160
+ console.error(` ${colors.error(figures.cross)} ${err.message}`);
161
+ logger.exit(1);
162
+ } else {
163
+ // Rethrow so it becomes an uncaught exception
164
+ throw err;
165
+ }
166
+ }
167
+ } else {
168
+ api.run(files)
169
+ .then(runStatus => {
170
+ logger.finish(runStatus);
171
+ logger.exit(runStatus.failCount > 0 || runStatus.rejectionCount > 0 || runStatus.exceptionCount > 0 ? 1 : 0);
172
+ })
173
+ .catch(err => {
174
+ // Don't swallow exceptions. Note that any expected error should already
175
+ // have been logged.
176
+ setImmediate(() => {
177
+ throw err;
178
+ });
179
+ });
180
+ }
181
+ };
@@ -0,0 +1,57 @@
1
+ 'use strict';
2
+ const fs = require('fs');
3
+ const equalLength = require('equal-length');
4
+ const codeExcerpt = require('code-excerpt');
5
+ const truncate = require('cli-truncate');
6
+ const chalk = require('chalk');
7
+
8
+ const formatLineNumber = (lineNumber, maxLineNumber) =>
9
+ ' '.repeat(Math.max(0, String(maxLineNumber).length - String(lineNumber).length)) + lineNumber;
10
+
11
+ module.exports = (source, options) => {
12
+ if (!source.isWithinProject || source.isDependency) {
13
+ return null;
14
+ }
15
+
16
+ const file = source.file;
17
+ const line = source.line;
18
+
19
+ options = options || {};
20
+ const maxWidth = options.maxWidth || 80;
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});
30
+ if (!excerpt) {
31
+ return null;
32
+ }
33
+
34
+ const lines = excerpt.map(item => ({
35
+ line: item.line,
36
+ value: truncate(item.value, maxWidth - String(line).length - 5)
37
+ }));
38
+
39
+ const joinedLines = lines.map(line => line.value).join('\n');
40
+ const extendedLines = equalLength(joinedLines).split('\n');
41
+
42
+ return lines
43
+ .map((item, index) => ({
44
+ line: item.line,
45
+ value: extendedLines[index]
46
+ }))
47
+ .map(item => {
48
+ const isErrorSource = item.line === line;
49
+
50
+ const lineNumber = formatLineNumber(item.line, line) + ':';
51
+ const coloredLineNumber = isErrorSource ? lineNumber : chalk.grey(lineNumber);
52
+ const result = ` ${coloredLineNumber} ${item.value}`;
53
+
54
+ return isErrorSource ? chalk.bgRed(result) : result;
55
+ })
56
+ .join('\n');
57
+ };
package/lib/colors.js CHANGED
@@ -1,11 +1,15 @@
1
1
  'use strict';
2
- var chalk = require('chalk');
2
+ const chalk = require('chalk');
3
3
 
4
4
  module.exports = {
5
+ title: chalk.bold.white,
5
6
  error: chalk.red,
6
7
  skip: chalk.yellow,
7
8
  todo: chalk.blue,
8
9
  pass: chalk.green,
9
10
  duration: chalk.gray.dim,
10
- stack: chalk.red
11
+ errorSource: chalk.gray,
12
+ errorStack: chalk.gray,
13
+ stack: chalk.red,
14
+ information: chalk.magenta
11
15
  };