diff --git a/.travis.yml b/.travis.yml index 1ad88015a..d41464c5b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,6 @@ node_js: - 11 - 10 - 8 - - 6 env: - FRESH_DEPS=false - FRESH_DEPS=true @@ -16,13 +15,9 @@ matrix: env: FRESH_DEPS=true - node_js: 8 env: FRESH_DEPS=true - - node_js: 6 - env: FRESH_DEPS=true - node_js: 10 env: FRESH_DEPS=true # Assume any bugs that occur with fresh dependencies are not platform specific. os: windows - - node_js: 6 - os: windows # npm install --global currently fails on Windows. Skip the tests entirely instead. cache: npm before_install: if [[ "$(npm -v)" != "6.9.0" ]] && [[ "${TRAVIS_OS_NAME}" != "windows" ]]; then npm install --global npm@6.9.0; fi install: npm ci diff --git a/bench/run.js b/bench/run.js index e87d7d27e..e5d95f3a3 100644 --- a/bench/run.js +++ b/bench/run.js @@ -100,29 +100,28 @@ for (let i = 0; i < 11; i++) { const results = {}; -Promise.each(combined, definition => { +Promise.each(combined, async definition => { const {args} = definition; - return runTests(args).then(result => { - const key = result.args.join(' '); - const passedOrFaild = result.err ? 'failed' : 'passed'; - const seconds = result.time / 1000; + const result = await runTests(args); + const key = result.args.join(' '); + const passedOrFaild = result.err ? 'failed' : 'passed'; + const seconds = result.time / 1000; - console.log('%s %s in %d seconds', key, passedOrFaild, seconds); + console.log('%s %s in %d seconds', key, passedOrFaild, seconds); - if (result.err && !definition.shouldFail) { - console.log(result.stdout); - console.log(result.stderr); - throw result.err; - } + if (result.err && !definition.shouldFail) { + console.log(result.stdout); + console.log(result.stderr); + throw result.err; + } - results[key] = results[key] || []; + results[key] = results[key] || []; - results[key].push({ - passed: !results.err, - shouldFail: definition.shouldFail, - time: seconds - }); + results[key].push({ + passed: !results.err, + shouldFail: definition.shouldFail, + time: seconds }); }).then(() => { makeDir.sync(path.join(__dirname, '.results')); diff --git a/docs/recipes/debugging-with-webstorm.md b/docs/recipes/debugging-with-webstorm.md index 27c5bef8e..7bf4237a6 100644 --- a/docs/recipes/debugging-with-webstorm.md +++ b/docs/recipes/debugging-with-webstorm.md @@ -13,7 +13,7 @@ In the `JavaScript file` field specify the path to AVA in the project's `node_mo In the `Application parameters` pass the CLI flags you're using and the test files you would like to debug, for example `--verbose test.js`. -In the `Node parameters`, for Node.js 7+, pass the `--inspect-brk` flag to enable the Node inspector. For earlier versions use `--debug-brk`. +In the `Node parameters`, pass the `--inspect-brk` flag to enable the Node inspector. Save the configuration. @@ -44,7 +44,7 @@ Use the following configuration parameters: Your IDE will then execute `npm run test` and thus call `node_modules/.bin/ava` and the AVA-configuration you have specified in your package.json. -In the `Node parameters`, for Node.js 7+ pass `--inspect-brk` or `--debug-brk` for earlier versions. +In the `Node parameters`, pass `--inspect-brk`. Don't forget to select a Node.js interpreter. diff --git a/lib/api.js b/lib/api.js index d9b986b66..52095b81b 100644 --- a/lib/api.js +++ b/lib/api.js @@ -14,8 +14,8 @@ const arrify = require('arrify'); const makeDir = require('make-dir'); const ms = require('ms'); const chunkd = require('chunkd'); +const Emittery = require('emittery'); const babelPipeline = require('./babel-pipeline'); -const Emittery = require('./emittery'); const RunStatus = require('./run-status'); const AvaFiles = require('./ava-files'); const fork = require('./fork'); @@ -37,7 +37,7 @@ class Api extends Emittery { constructor(options) { super(); - this.options = Object.assign({match: []}, options); + this.options = {match: [], ...options}; this.options.require = resolveModules(this.options.require); this._allExtensions = this.options.extensions.all; @@ -50,7 +50,7 @@ class Api extends Emittery { } } - run(files, runtimeOptions = {}) { + async run(files, runtimeOptions = {}) { const apiOptions = this.options; // Each run will have its own status. It can only be created when test files @@ -104,152 +104,141 @@ class Api extends Emittery { } }; - // Find all test files. - return new AvaFiles({cwd: apiOptions.resolveTestsFrom, files, extensions: this._allExtensions}).findTestFiles() - .then(files => { - if (this.options.parallelRuns) { - const {currentIndex, totalRuns} = this.options.parallelRuns; - const fileCount = files.length; + try { + // Find all test files. + files = await new AvaFiles({cwd: apiOptions.resolveTestsFrom, files, extensions: this._allExtensions}).findTestFiles(); + if (this.options.parallelRuns) { + const {currentIndex, totalRuns} = this.options.parallelRuns; + const fileCount = files.length; - // The files must be in the same order across all runs, so sort them. - files = files.sort((a, b) => a.localeCompare(b, [], {numeric: true})); - files = chunkd(files, currentIndex, totalRuns); + // The files must be in the same order across all runs, so sort them. + files = files.sort((a, b) => a.localeCompare(b, [], {numeric: true})); + files = chunkd(files, currentIndex, totalRuns); - const currentFileCount = files.length; + const currentFileCount = files.length; - runStatus = new RunStatus(fileCount, {currentFileCount, currentIndex, totalRuns}); - } else { - runStatus = new RunStatus(files.length, null); - } + runStatus = new RunStatus(fileCount, {currentFileCount, currentIndex, totalRuns}); + } else { + runStatus = new RunStatus(files.length, null); + } - const emittedRun = this.emit('run', { - clearLogOnNextRun: runtimeOptions.clearLogOnNextRun === true, - failFastEnabled: failFast, - filePathPrefix: commonPathPrefix(files), - files, - matching: apiOptions.match.length > 0, - previousFailures: runtimeOptions.previousFailures || 0, - runOnlyExclusive: runtimeOptions.runOnlyExclusive === true, - runVector: runtimeOptions.runVector || 0, - status: runStatus - }); + await this.emit('run', { + clearLogOnNextRun: runtimeOptions.clearLogOnNextRun === true, + failFastEnabled: failFast, + filePathPrefix: commonPathPrefix(files), + files, + matching: apiOptions.match.length > 0, + previousFailures: runtimeOptions.previousFailures || 0, + runOnlyExclusive: runtimeOptions.runOnlyExclusive === true, + runVector: runtimeOptions.runVector || 0, + status: runStatus + }); - // Bail out early if no files were found. - if (files.length === 0) { - return emittedRun.then(() => { - return runStatus; - }); - } + // Bail out early if no files were found. + if (files.length === 0) { + return runStatus; + } - runStatus.on('stateChange', record => { - if (record.testFile && !timedOutWorkerFiles.has(record.testFile)) { - // Restart the timer whenever there is activity from workers that - // haven't already timed out. - restartTimer(); - } + runStatus.on('stateChange', record => { + if (record.testFile && !timedOutWorkerFiles.has(record.testFile)) { + // Restart the timer whenever there is activity from workers that + // haven't already timed out. + restartTimer(); + } - if (failFast && (record.type === 'hook-failed' || record.type === 'test-failed' || record.type === 'worker-failed')) { - // Prevent new test files from running once a test has failed. - bailed = true; + if (failFast && (record.type === 'hook-failed' || record.type === 'test-failed' || record.type === 'worker-failed')) { + // Prevent new test files from running once a test has failed. + bailed = true; - // Try to stop currently scheduled tests. - for (const worker of pendingWorkers) { - worker.notifyOfPeerFailure(); - } + // Try to stop currently scheduled tests. + for (const worker of pendingWorkers) { + worker.notifyOfPeerFailure(); } - }); + } + }); - return emittedRun - .then(() => this._setupPrecompiler()) - .then(precompilation => { - if (!precompilation.enabled) { - return null; + let precompilation = await this._setupPrecompiler(); + if (precompilation.enabled) { + // Compile all test and helper files. Assumes the tests only load + // helpers from within the `resolveTestsFrom` directory. Without + // arguments this is the `projectDir`, else it's `process.cwd()` + // which may be nested too deeply. + const helpers = await new AvaFiles({cwd: this.options.resolveTestsFrom, extensions: this._allExtensions}).findTestHelpers(); + precompilation = { + cacheDir: precompilation.cacheDir, + map: [...files, ...helpers].reduce((acc, file) => { + try { + const realpath = fs.realpathSync(file); + const filename = path.basename(realpath); + const cachePath = this._regexpFullExtensions.test(filename) ? + precompilation.precompileFull(realpath) : + precompilation.precompileEnhancementsOnly(realpath); + if (cachePath) { + acc[realpath] = cachePath; + } + } catch (error) { + throw Object.assign(error, {file}); } - // Compile all test and helper files. Assumes the tests only load - // helpers from within the `resolveTestsFrom` directory. Without - // arguments this is the `projectDir`, else it's `process.cwd()` - // which may be nested too deeply. - return new AvaFiles({cwd: this.options.resolveTestsFrom, extensions: this._allExtensions}) - .findTestHelpers().then(helpers => { - return { - cacheDir: precompilation.cacheDir, - map: [...files, ...helpers].reduce((acc, file) => { - try { - const realpath = fs.realpathSync(file); - const filename = path.basename(realpath); - const cachePath = this._regexpFullExtensions.test(filename) ? - precompilation.precompileFull(realpath) : - precompilation.precompileEnhancementsOnly(realpath); - if (cachePath) { - acc[realpath] = cachePath; - } - } catch (error) { - throw Object.assign(error, {file}); - } - - return acc; - }, {}) - }; - }); - }) - .then(precompilation => { - // Resolve the correct concurrency value. - let concurrency = Math.min(os.cpus().length, isCi ? 2 : Infinity); - if (apiOptions.concurrency > 0) { - concurrency = apiOptions.concurrency; - } + return acc; + }, {}) + }; + } else { + precompilation = null; + } - if (apiOptions.serial) { - concurrency = 1; - } + // Resolve the correct concurrency value. + let concurrency = Math.min(os.cpus().length, isCi ? 2 : Infinity); + if (apiOptions.concurrency > 0) { + concurrency = apiOptions.concurrency; + } - // Try and run each file, limited by `concurrency`. - return Bluebird.map(files, file => { - // No new files should be run once a test has timed out or failed, - // and failFast is enabled. - if (bailed) { - return; - } + if (apiOptions.serial) { + concurrency = 1; + } - return this._computeForkExecArgv().then(execArgv => { - const options = Object.assign({}, apiOptions, { - // If we're looking for matches, run every single test process in exclusive-only mode - runOnlyExclusive: apiOptions.match.length > 0 || runtimeOptions.runOnlyExclusive === true - }); - if (precompilation) { - options.cacheDir = precompilation.cacheDir; - options.precompiled = precompilation.map; - } else { - options.precompiled = {}; - } - - if (runtimeOptions.updateSnapshots) { - // Don't use in Object.assign() since it'll override options.updateSnapshots even when false. - options.updateSnapshots = true; - } - - const worker = fork(file, options, execArgv); - runStatus.observeWorker(worker, file); - - pendingWorkers.add(worker); - worker.promise.then(() => { // eslint-disable-line max-nested-callbacks - pendingWorkers.delete(worker); - }); - restartTimer(); - - return worker.promise; - }); - }, {concurrency}); - }) - .catch(err => { - runStatus.emitStateChange({type: 'internal-error', err: serializeError('Internal error', false, err)}); - }) - .then(() => { - restartTimer.cancel(); - return runStatus; - }); - }); + // Try and run each file, limited by `concurrency`. + await Bluebird.map(files, async file => { + // No new files should be run once a test has timed out or failed, + // and failFast is enabled. + if (bailed) { + return; + } + + const execArgv = await this._computeForkExecArgv(); + const options = { + ...apiOptions, // If we're looking for matches, run every single test process in exclusive-only mode + runOnlyExclusive: apiOptions.match.length > 0 || runtimeOptions.runOnlyExclusive === true + }; + if (precompilation) { + options.cacheDir = precompilation.cacheDir; + options.precompiled = precompilation.map; + } else { + options.precompiled = {}; + } + + if (runtimeOptions.updateSnapshots) { + // Don't use in Object.assign() since it'll override options.updateSnapshots even when false. + options.updateSnapshots = true; + } + + const worker = fork(file, options, execArgv); + runStatus.observeWorker(worker, file); + + pendingWorkers.add(worker); + worker.promise.then(() => { + pendingWorkers.delete(worker); + }); + restartTimer(); + + return worker.promise; + }, {concurrency}); + } catch (error) { + runStatus.emitStateChange({type: 'internal-error', err: serializeError('Internal error', false, error)}); + } + + restartTimer.cancel(); + return runStatus; } _setupPrecompiler() { @@ -290,52 +279,29 @@ class Api extends Emittery { return this._precompiler; } - _computeForkExecArgv() { + async _computeForkExecArgv() { const execArgv = this.options.testOnlyExecArgv || process.execArgv; if (execArgv.length === 0) { return Promise.resolve(execArgv); } - let debugArgIndex = -1; - // --inspect-brk is used in addition to --inspect to break on first line and wait - execArgv.some((arg, index) => { - const isDebugArg = /^--inspect(-brk)?($|=)/.test(arg); - if (isDebugArg) { - debugArgIndex = index; - } - - return isDebugArg; - }); - - const isInspect = debugArgIndex >= 0; - if (!isInspect) { - execArgv.some((arg, index) => { - const isDebugArg = /^--debug(-brk)?($|=)/.test(arg); - if (isDebugArg) { - debugArgIndex = index; - } - - return isDebugArg; - }); - } - - if (debugArgIndex === -1) { + const inspectArgIndex = execArgv.findIndex(arg => /^--inspect(-brk)?($|=)/.test(arg)); + if (inspectArgIndex === -1) { return Promise.resolve(execArgv); } - return getPort().then(port => { - const forkExecArgv = execArgv.slice(); - let flagName = isInspect ? '--inspect' : '--debug'; - const oldValue = forkExecArgv[debugArgIndex]; - if (oldValue.indexOf('brk') > 0) { - flagName += '-brk'; - } + const port = await getPort(); + const forkExecArgv = execArgv.slice(); + let flagName = '--inspect'; + const oldValue = forkExecArgv[inspectArgIndex]; + if (oldValue.indexOf('brk') > 0) { + flagName += '-brk'; + } - forkExecArgv[debugArgIndex] = `${flagName}=${port}`; + forkExecArgv[inspectArgIndex] = `${flagName}=${port}`; - return forkExecArgv; - }); + return forkExecArgv; } } diff --git a/lib/assert.js b/lib/assert.js index 1a9dccf11..9868d6224 100644 --- a/lib/assert.js +++ b/lib/assert.js @@ -8,7 +8,7 @@ const enhanceAssert = require('./enhance-assert'); const snapshotManager = require('./snapshot-manager'); function formatDescriptorDiff(actualDescriptor, expectedDescriptor, options) { - options = Object.assign({}, options, concordanceDiffOptions); + options = {...options, ...concordanceDiffOptions}; return { label: 'Difference:', formatted: concordance.diffDescriptors(actualDescriptor, expectedDescriptor, options) @@ -400,7 +400,7 @@ function wrapAssertions(callbacks) { const handlePromise = (promise, wasReturned) => { // Record stack before it gets lost in the promise chain. const stack = getStack(); - const intermediate = promise.then(value => { + const intermediate = promise.then(value => { // eslint-disable-line promise/prefer-await-to-then throw new AssertionError({ assertion: 'throwsAsync', message, @@ -500,7 +500,7 @@ function wrapAssertions(callbacks) { const handlePromise = (promise, wasReturned) => { // Record stack before it gets lost in the promise chain. const stack = getStack(); - const intermediate = promise.then(noop, reason => { + const intermediate = promise.then(noop, reason => { // eslint-disable-line promise/prefer-await-to-then throw new AssertionError({ assertion: 'notThrowsAsync', message, diff --git a/lib/ava-files.js b/lib/ava-files.js index fa86436dc..7d16a26ab 100644 --- a/lib/ava-files.js +++ b/lib/ava-files.js @@ -56,7 +56,7 @@ function handlePaths(files, extensions, excludePatterns, globOptions) { // paths are consistently platform-accurate as tests are run. return path.normalize(file); }) - .then(flatten) + .then(flatten) // eslint-disable-line promise/prefer-await-to-then .filter(file => file && extensions.includes(path.extname(file).substr(1))) .filter(file => { if (path.basename(file)[0] === '_' && globOptions.includeUnderscoredFiles !== true) { @@ -132,20 +132,22 @@ class AvaFiles { } findTestFiles() { - return handlePaths(this.files, this.extensions, this.excludePatterns, Object.assign({ + return handlePaths(this.files, this.extensions, this.excludePatterns, { cwd: this.cwd, expandDirectories: false, - nodir: false - }, this.globCaches)); + nodir: false, + ...this.globCaches + }); } findTestHelpers() { - return handlePaths(defaultHelperPatterns(this.extensionPattern), this.extensions, ['!**/node_modules/**'], Object.assign({ + return handlePaths(defaultHelperPatterns(this.extensionPattern), this.extensions, ['!**/node_modules/**'], { cwd: this.cwd, includeUnderscoredFiles: true, expandDirectories: false, - nodir: false - }, this.globCaches)); + nodir: false, + ...this.globCaches + }); } isSource(filePath) { diff --git a/lib/babel-pipeline.js b/lib/babel-pipeline.js index a00954f85..1b5513cdb 100644 --- a/lib/babel-pipeline.js +++ b/lib/babel-pipeline.js @@ -55,7 +55,7 @@ function validate(conf) { return { extensions: conf.extensions, - testOptions: Object.assign({}, defaultOptions, conf.testOptions) + testOptions: {...defaultOptions, ...conf.testOptions} }; } @@ -176,18 +176,18 @@ function build(projectDir, cacheDir, userOptions, compileEnhancements) { const containsTransformTestFiles = makeValueChecker('@ava/babel-preset-transform-test-files'); const loadOptions = filename => { - const partialTestConfig = babel.loadPartialConfig(Object.assign({ + const partialTestConfig = babel.loadPartialConfig({ babelrc: false, babelrcRoots: [projectDir], configFile: false, - sourceMaps: true - }, userOptions && userOptions.testOptions, { + sourceMaps: true, + ...userOptions && userOptions.testOptions, cwd: projectDir, envName, filename, sourceFileName: path.relative(projectDir, filename), sourceRoot: projectDir - })); + }); if (!partialTestConfig) { return {hash: '', options: null}; diff --git a/lib/cli.js b/lib/cli.js index 00d7e36ee..c7cfb6388 100644 --- a/lib/cli.js +++ b/lib/cli.js @@ -17,7 +17,7 @@ function exit(message) { process.exit(1); // eslint-disable-line unicorn/no-process-exit } -exports.run = () => { // eslint-disable-line complexity +exports.run = async () => { // eslint-disable-line complexity let conf = {}; let confError = null; try { @@ -124,15 +124,17 @@ exports.run = () => { // eslint-disable-line complexity const {projectDir} = conf; if (cli.flags.resetCache) { const cacheDir = path.join(projectDir, 'node_modules', '.cache', 'ava'); - del('*', { - cwd: cacheDir, - nodir: true - }).then(() => { + try { + await del('*', { + cwd: cacheDir, + nodir: true + }); console.error(`\n${chalk.green(figures.tick)} Removed AVA cache files in ${cacheDir}`); process.exit(0); // eslint-disable-line unicorn/no-process-exit - }, error => { + } catch (error) { exit(`Error removing AVA cache files in ${cacheDir}\n\n${chalk.gray((error && error.stack) || error)}`); - }); + } + return; } @@ -255,9 +257,8 @@ exports.run = () => { // eslint-disable-line complexity }); watcher.observeStdin(process.stdin); } else { - api.run(files).then(runStatus => { - process.exitCode = runStatus.suggestExitCode({matching: match.length > 0}); - reporter.endRun(); - }); + const runStatus = await api.run(files); + process.exitCode = runStatus.suggestExitCode({matching: match.length > 0}); + reporter.endRun(); } }; diff --git a/lib/concordance-options.js b/lib/concordance-options.js index b934ab58a..b3390a4b3 100644 --- a/lib/concordance-options.js +++ b/lib/concordance-options.js @@ -7,7 +7,7 @@ const chalk = require('./chalk').get(); // Wrap Concordance's React plugin. Change the name to avoid collisions if in // the future users can register plugins themselves. -const avaReactPlugin = Object.assign({}, reactPlugin, {name: 'ava-plugin-react'}); +const avaReactPlugin = {...reactPlugin, name: 'ava-plugin-react'}; const plugins = [avaReactPlugin]; const forceColor = new chalk.constructor({enabled: true}); diff --git a/lib/create-chain.js b/lib/create-chain.js index 481a9e457..aea68a622 100644 --- a/lib/create-chain.js +++ b/lib/create-chain.js @@ -3,7 +3,7 @@ const chainRegistry = new WeakMap(); function startChain(name, call, defaults) { const fn = (...args) => { - call(Object.assign({}, defaults), args); + call({...defaults}, args); }; Object.defineProperty(fn, 'name', {value: name}); @@ -33,7 +33,7 @@ function callWithFlag(prev, flag, args) { do { const step = chainRegistry.get(prev); if (step.call) { - step.call(Object.assign({}, step.defaults, combinedFlags), args); + step.call({...step.defaults, ...combinedFlags}, args); prev = null; } else { combinedFlags[step.flag] = true; @@ -68,7 +68,7 @@ function createChain(fn, defaults, meta) { // * `failing` must come at the end, but can be followed by `only` and `skip` // * `only` and `skip` cannot be chained together // * no repeating - const root = startChain('test', fn, Object.assign({}, defaults, {type: 'test'})); + const root = startChain('test', fn, {...defaults, type: 'test'}); extendChain(root, 'cb', 'callback'); extendChain(root, 'failing'); extendChain(root, 'only', 'exclusive'); @@ -93,20 +93,20 @@ function createChain(fn, defaults, meta) { extendChain(root.serial.failing, 'only', 'exclusive'); extendChain(root.serial.failing, 'skip', 'skipped'); - root.after = createHookChain(startChain('test.after', fn, Object.assign({}, defaults, {type: 'after'})), true); - root.afterEach = createHookChain(startChain('test.afterEach', fn, Object.assign({}, defaults, {type: 'afterEach'})), true); - root.before = createHookChain(startChain('test.before', fn, Object.assign({}, defaults, {type: 'before'})), false); - root.beforeEach = createHookChain(startChain('test.beforeEach', fn, Object.assign({}, defaults, {type: 'beforeEach'})), false); + root.after = createHookChain(startChain('test.after', fn, {...defaults, type: 'after'}), true); + root.afterEach = createHookChain(startChain('test.afterEach', fn, {...defaults, type: 'afterEach'}), true); + root.before = createHookChain(startChain('test.before', fn, {...defaults, type: 'before'}), false); + root.beforeEach = createHookChain(startChain('test.beforeEach', fn, {...defaults, type: 'beforeEach'}), false); - root.serial.after = createHookChain(startChain('test.after', fn, Object.assign({}, defaults, {serial: true, type: 'after'})), true); - root.serial.afterEach = createHookChain(startChain('test.afterEach', fn, Object.assign({}, defaults, {serial: true, type: 'afterEach'})), true); - root.serial.before = createHookChain(startChain('test.before', fn, Object.assign({}, defaults, {serial: true, type: 'before'})), false); - root.serial.beforeEach = createHookChain(startChain('test.beforeEach', fn, Object.assign({}, defaults, {serial: true, type: 'beforeEach'})), false); + root.serial.after = createHookChain(startChain('test.after', fn, {...defaults, serial: true, type: 'after'}), true); + root.serial.afterEach = createHookChain(startChain('test.afterEach', fn, {...defaults, serial: true, type: 'afterEach'}), true); + root.serial.before = createHookChain(startChain('test.before', fn, {...defaults, serial: true, type: 'before'}), false); + root.serial.beforeEach = createHookChain(startChain('test.beforeEach', fn, {...defaults, serial: true, type: 'beforeEach'}), false); // "todo" tests cannot be chained. Allow todo tests to be flagged as needing // to be serial. - root.todo = startChain('test.todo', fn, Object.assign({}, defaults, {type: 'test', todo: true})); - root.serial.todo = startChain('test.serial.todo', fn, Object.assign({}, defaults, {serial: true, type: 'test', todo: true})); + root.todo = startChain('test.todo', fn, {...defaults, type: 'test', todo: true}); + root.serial.todo = startChain('test.serial.todo', fn, {...defaults, serial: true, type: 'test', todo: true}); root.meta = meta; diff --git a/lib/emittery.js b/lib/emittery.js deleted file mode 100644 index 4c1b161fd..000000000 --- a/lib/emittery.js +++ /dev/null @@ -1,6 +0,0 @@ -'use strict'; -try { - module.exports = require('emittery'); -} catch (_) { - module.exports = require('emittery/legacy'); -} diff --git a/lib/fork.js b/lib/fork.js index 443cf2c4e..6a12a9083 100644 --- a/lib/fork.js +++ b/lib/fork.js @@ -3,13 +3,13 @@ const childProcess = require('child_process'); const path = require('path'); const fs = require('fs'); const Promise = require('bluebird'); -const Emittery = require('./emittery'); +const Emittery = require('emittery'); if (fs.realpathSync(__filename) !== __filename) { 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`.'); } -const env = Object.assign({NODE_ENV: 'test'}, process.env); +const env = {NODE_ENV: 'test', ...process.env}; // Ensure NODE_PATH paths are absolute if (env.NODE_PATH) { @@ -41,14 +41,15 @@ module.exports = (file, opts, execArgv) => { } }; - opts = Object.assign({ + opts = { file, baseDir: process.cwd(), tty: { stderr: process.stderr.isTTY ? describeTTY(process.stderr) : false, stdout: process.stdout.isTTY ? describeTTY(process.stdout) : false - } - }, opts); + }, + ...opts + }; const args = [opts.color ? '--color' : '--no-color'].concat(opts.workerArgv); diff --git a/lib/load-config.js b/lib/load-config.js index 4ad33f135..eafcaf7b6 100644 --- a/lib/load-config.js +++ b/lib/load-config.js @@ -44,7 +44,7 @@ function loadConfig(defaults = {}) { throw new Error('Conflicting configuration in ava.config.js and package.json'); } - if (fileConf && typeof fileConf.then === 'function') { + if (fileConf && typeof fileConf.then === 'function') { // eslint-disable-line promise/prefer-await-to-then throw new TypeError('ava.config.js must not export a promise'); } @@ -54,7 +54,7 @@ function loadConfig(defaults = {}) { if (typeof fileConf === 'function') { fileConf = fileConf({projectDir}); - if (fileConf && typeof fileConf.then === 'function') { + if (fileConf && typeof fileConf.then === 'function') { // eslint-disable-line promise/prefer-await-to-then throw new TypeError('Factory method exported by ava.config.js must not return a promise'); } @@ -68,7 +68,7 @@ function loadConfig(defaults = {}) { } } - return Object.assign({}, defaults, fileConf, packageConf, {projectDir}); + return {...defaults, ...fileConf, ...packageConf, projectDir}; } module.exports = loadConfig; diff --git a/lib/reporters/tap.js b/lib/reporters/tap.js index 708a08791..9955eed8e 100644 --- a/lib/reporters/tap.js +++ b/lib/reporters/tap.js @@ -9,7 +9,7 @@ const supertap = require('supertap'); const prefixTitle = require('./prefix-title'); function dumpError(error) { - const obj = Object.assign({}, error.object); + const obj = {...error.object}; if (error.name) { obj.name = error.name; } diff --git a/lib/run-status.js b/lib/run-status.js index 336f9c3f6..e32239ef3 100644 --- a/lib/run-status.js +++ b/lib/run-status.js @@ -1,6 +1,6 @@ 'use strict'; +const Emittery = require('emittery'); const cloneDeep = require('lodash.clonedeep'); -const Emittery = require('./emittery'); class RunStatus extends Emittery { constructor(files, parallelRuns) { diff --git a/lib/runner.js b/lib/runner.js index 35d65125b..6457fbaff 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -1,8 +1,8 @@ 'use strict'; +const Emittery = require('emittery'); const matcher = require('matcher'); const ContextRef = require('./context-ref'); const createChain = require('./create-chain'); -const Emittery = require('./emittery'); const snapshotManager = require('./snapshot-manager'); const serializeError = require('./serialize-error'); const Runnable = require('./test'); @@ -129,7 +129,7 @@ class Runner extends Emittery { title, implementation, args, - metadata: Object.assign({}, metadata) + metadata: {...metadata} }; if (metadata.type === 'test') { @@ -217,21 +217,20 @@ class Runner extends Emittery { } } - runMultiple(runnables) { + async runMultiple(runnables) { let allPassed = true; const storedResults = []; - const runAndStoreResult = runnable => { - return this.runSingle(runnable).then(result => { - if (!result.passed) { - allPassed = false; - } + const runAndStoreResult = async runnable => { + const result = await this.runSingle(runnable); + if (!result.passed) { + allPassed = false; + } - storedResults.push(result); - }); + storedResults.push(result); }; let waitForSerial = Promise.resolve(); - return runnables.reduce((prev, runnable) => { + await runnables.reduce((prev, runnable) => { if (runnable.metadata.serial || this.serial) { waitForSerial = prev.then(() => { // Serial runnables run as long as there was no previous failure, unless @@ -251,20 +250,21 @@ class Runner extends Emittery { return (allPassed || runnable.metadata.always) && runAndStoreResult(runnable); }) ]); - }, waitForSerial).then(() => ({allPassed, storedResults})); + }, waitForSerial); + + return {allPassed, storedResults}; } - runSingle(runnable) { + async runSingle(runnable) { this.onRun(runnable); - return runnable.run().then(result => { - // If run() throws or rejects then the entire test run crashes, so - // onRunComplete() doesn't *have* to be inside a finally(). - this.onRunComplete(runnable); - return result; - }); + const result = await runnable.run(); + // If run() throws or rejects then the entire test run crashes, so + // onRunComplete() doesn't *have* to be inside a finally. + this.onRunComplete(runnable); + return result; } - runHooks(tasks, contextRef, titleSuffix) { + async runHooks(tasks, contextRef, titleSuffix) { const hooks = tasks.map(task => new Runnable({ contextRef, failWithoutAssertions: false, @@ -276,38 +276,35 @@ class Runner extends Emittery { metadata: task.metadata, title: `${task.title}${titleSuffix || ''}` })); - return this.runMultiple(hooks, this.serial).then(outcome => { - for (const result of outcome.storedResults) { - if (result.passed) { - this.emit('stateChange', { - type: 'hook-finished', - title: result.title, - duration: result.duration, - logs: result.logs - }); - } else { - this.emit('stateChange', { - type: 'hook-failed', - title: result.title, - err: serializeError('Hook failure', true, result.error), - duration: result.duration, - logs: result.logs - }); - } + const outcome = await this.runMultiple(hooks, this.serial); + for (const result of outcome.storedResults) { + if (result.passed) { + this.emit('stateChange', { + type: 'hook-finished', + title: result.title, + duration: result.duration, + logs: result.logs + }); + } else { + this.emit('stateChange', { + type: 'hook-failed', + title: result.title, + err: serializeError('Hook failure', true, result.error), + duration: result.duration, + logs: result.logs + }); } + } - return outcome.allPassed; - }); + return outcome.allPassed; } - runTest(task, contextRef) { - const hookSuffix = ` for ${task.title}`; - return this.runHooks(this.tasks.beforeEach, contextRef, hookSuffix).then(hooksOk => { - // Don't run the test if a `beforeEach` hook failed. - if (!hooksOk) { - return false; - } + async runTest(task, contextRef) { + let hooksAndTestOk = false; + const hookSuffix = ` for ${task.title}`; + if (await this.runHooks(this.tasks.beforeEach, contextRef, hookSuffix)) { + // Only run the test if all `beforeEach` hooks passed. const test = new Runnable({ contextRef, failWithoutAssertions: this.failWithoutAssertions, @@ -319,18 +316,18 @@ class Runner extends Emittery { metadata: task.metadata, title: task.title }); - return this.runSingle(test).then(result => { - if (result.passed) { - this.emit('stateChange', { - type: 'test-passed', - title: result.title, - duration: result.duration, - knownFailing: result.metadata.failing, - logs: result.logs - }); - return this.runHooks(this.tasks.afterEach, contextRef, hookSuffix); - } + const result = await this.runSingle(test); + if (result.passed) { + this.emit('stateChange', { + type: 'test-passed', + title: result.title, + duration: result.duration, + knownFailing: result.metadata.failing, + logs: result.logs + }); + hooksAndTestOk = await this.runHooks(this.tasks.afterEach, contextRef, hookSuffix); + } else { this.emit('stateChange', { type: 'test-failed', title: result.title, @@ -340,16 +337,14 @@ class Runner extends Emittery { logs: result.logs }); // Don't run `afterEach` hooks if the test failed. - return false; - }); - }).then(hooksAndTestOk => { - return this.runHooks(this.tasks.afterEachAlways, contextRef, hookSuffix).then(alwaysOk => { - return hooksAndTestOk && alwaysOk; - }); - }); + } + } + + const alwaysOk = await this.runHooks(this.tasks.afterEachAlways, contextRef, hookSuffix); + return hooksAndTestOk && alwaysOk; } - start() { + async start() { const concurrentTests = []; const serialTests = []; for (const task of this.tasks.serial) { @@ -416,32 +411,29 @@ class Runner extends Emittery { // Note that the hooks and tests always begin running asynchronously. const beforePromise = this.runHooks(this.tasks.before, contextRef); - const serialPromise = beforePromise.then(beforeHooksOk => { + const serialPromise = beforePromise.then(beforeHooksOk => { // eslint-disable-line promise/prefer-await-to-then // Don't run tests if a `before` hook failed. if (!beforeHooksOk) { return false; } - return serialTests.reduce((prev, task) => { - return prev.then(prevOk => { - // Don't start tests after an interrupt. - if (this.interrupted) { - return prevOk; - } + return serialTests.reduce(async (prev, task) => { + const prevOk = await prev; + // Don't start tests after an interrupt. + if (this.interrupted) { + return prevOk; + } - // Prevent subsequent tests from running if `failFast` is enabled and - // the previous test failed. - if (!prevOk && this.failFast) { - return false; - } + // Prevent subsequent tests from running if `failFast` is enabled and + // the previous test failed. + if (!prevOk && this.failFast) { + return false; + } - return this.runTest(task, contextRef.copy()); - }); - }, Promise.resolve(true)); + return this.runTest(task, contextRef.copy()); + }, true); }); - const concurrentPromise = Promise.all([beforePromise, serialPromise]).then(prevOkays => { - const beforeHooksOk = prevOkays[0]; - const serialOk = prevOkays[1]; + const concurrentPromise = Promise.all([beforePromise, serialPromise]).then(async ([beforeHooksOk, serialOk]) => { // eslint-disable-line promise/prefer-await-to-then // Don't run tests if a `before` hook failed, or if `failFast` is enabled // and a previous serial test failed. if (!beforeHooksOk || (!serialOk && this.failFast)) { @@ -455,23 +447,29 @@ class Runner extends Emittery { // If a concurrent test fails, even if `failFast` is enabled it won't // stop other concurrent tests from running. - return Promise.all(concurrentTests.map(task => { + const allOkays = await Promise.all(concurrentTests.map(task => { return this.runTest(task, contextRef.copy()); - })).then(allOkays => allOkays.every(ok => ok)); + })); + return allOkays.every(ok => ok); }); const beforeExitHandler = this.beforeExitHandler.bind(this); process.on('beforeExit', beforeExitHandler); - concurrentPromise + try { + const ok = await concurrentPromise; // Only run `after` hooks if all hooks and tests passed. - .then(ok => ok && this.runHooks(this.tasks.after, contextRef)) + if (ok) { + await this.runHooks(this.tasks.after, contextRef); + } + // Always run `after.always` hooks. - .then(() => this.runHooks(this.tasks.afterAlways, contextRef)) - .then(() => { - process.removeListener('beforeExit', beforeExitHandler); - }) - .then(() => this.emit('finish'), err => this.emit('error', err)); + await this.runHooks(this.tasks.afterAlways, contextRef); + process.removeListener('beforeExit', beforeExitHandler); + await this.emit('finish'); + } catch (error) { + await this.emit('error', error); + } } interrupt() { diff --git a/lib/test.js b/lib/test.js index ad86c1c4f..dd7e9828f 100644 --- a/lib/test.js +++ b/lib/test.js @@ -215,7 +215,7 @@ class Test { promise .catch(error => this.saveFirstError(error)) - .then(() => { + .then(() => { // eslint-disable-line promise/prefer-await-to-then this.pendingAssertionCount--; this.refreshTimeout(); }); @@ -457,7 +457,7 @@ class Test { })); } }) - .then(() => resolve(this.finishPromised())); + .then(() => resolve(this.finishPromised())); // eslint-disable-line promise/prefer-await-to-then }); } diff --git a/lib/watcher.js b/lib/watcher.js index 2110809fb..cf55036f2 100644 --- a/lib/watcher.js +++ b/lib/watcher.js @@ -36,24 +36,23 @@ class Debouncer { delay = delay ? Math.max(delay, MIN_DEBOUNCE_DELAY) : INITIAL_DEBOUNCE_DELAY; - const timer = setTimeout(() => { - this.watcher.busy.then(() => { - // Do nothing if debouncing was canceled while waiting for the busy - // promise to fulfil - if (this.timer !== timer) { - return; - } + const timer = setTimeout(async () => { + await this.watcher.busy; + // Do nothing if debouncing was canceled while waiting for the busy + // promise to fulfil + if (this.timer !== timer) { + return; + } - if (this.again) { - this.timer = null; - this.again = false; - this.debounce(delay / 2); - } else { - this.watcher.runAfterChanges(); - this.timer = null; - this.again = false; - } - }); + if (this.again) { + this.timer = null; + this.again = false; + this.debounce(delay / 2); + } else { + this.watcher.runAfterChanges(); + this.timer = null; + this.again = false; + } }, delay); this.timer = timer; @@ -123,7 +122,7 @@ class Watcher { runVector: this.runVector, updateSnapshots: updateSnapshots === true }) - .then(runStatus => { + .then(runStatus => { // eslint-disable-line promise/prefer-await-to-then reporter.endRun(); reporter.lineWriter.writeLine(END_MESSAGE); @@ -321,7 +320,7 @@ class Watcher { stdin.resume(); stdin.setEncoding('utf8'); - stdin.on('data', data => { + stdin.on('data', async data => { data = data.trim().toLowerCase(); if (data !== 'r' && data !== 'rs' && data !== 'u') { return; @@ -330,17 +329,16 @@ class Watcher { // Cancel the debouncer, it might rerun specific tests whereas *all* tests // need to be rerun this.debouncer.cancel(); - this.busy.then(() => { - // Cancel the debouncer again, it might have restarted while waiting for - // the busy promise to fulfil - this.debouncer.cancel(); - this.clearLogOnNextRun = false; - if (data === 'u') { - this.updatePreviousSnapshots(); - } else { - this.rerunAll(); - } - }); + await this.busy; + // Cancel the debouncer again, it might have restarted while waiting for + // the busy promise to fulfil + this.debouncer.cancel(); + this.clearLogOnNextRun = false; + if (data === 'u') { + this.updatePreviousSnapshots(); + } else { + this.rerunAll(); + } }); } diff --git a/lib/worker/ipc.js b/lib/worker/ipc.js index 7b68a4426..2eb5bda86 100644 --- a/lib/worker/ipc.js +++ b/lib/worker/ipc.js @@ -1,9 +1,5 @@ 'use strict'; -const Emittery = require('../emittery'); - -// `process.channel` was added in Node.js 7.1.0, but the channel was available -// through an undocumented API as `process._channel`. -const channel = process.channel || process._channel; +const Emittery = require('emittery'); const emitter = new Emittery(); process.on('message', message => { @@ -38,24 +34,23 @@ function send(evt) { exports.send = send; function unref() { - channel.unref(); + process.channel.unref(); } exports.unref = unref; let pendingPings = Promise.resolve(); -function flush() { - channel.ref(); - const promise = pendingPings.then(() => { +async function flush() { + process.channel.ref(); + const promise = pendingPings.then(async () => { // eslint-disable-line promise/prefer-await-to-then send({type: 'ping'}); - return emitter.once('pong'); - }).then(() => { + await emitter.once('pong'); if (promise === pendingPings) { unref(); } }); pendingPings = promise; - return promise; + await promise; } exports.flush = flush; diff --git a/lib/worker/subprocess.js b/lib/worker/subprocess.js index aaafedb9e..4b0497ff4 100644 --- a/lib/worker/subprocess.js +++ b/lib/worker/subprocess.js @@ -20,13 +20,14 @@ ipc.options.then(options => { const dependencyTracking = require('./dependency-tracker'); const precompilerHook = require('./precompiler-hook'); - function exit(code) { + async function exit(code) { if (!process.exitCode) { process.exitCode = code; } dependencyTracking.flush(); - return ipc.flush().then(() => process.exit()); // eslint-disable-line unicorn/no-process-exit + await ipc.flush(); + process.exit(); // eslint-disable-line unicorn/no-process-exit } const runner = new Runner({ @@ -41,7 +42,7 @@ ipc.options.then(options => { updateSnapshots: options.updateSnapshots }); - ipc.peerFailed.then(() => { + ipc.peerFailed.then(() => { // eslint-disable-line promise/prefer-await-to-then runner.interrupt(); }); diff --git a/package.json b/package.json index 76ccded04..0e2d2d543 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "homepage": "https://ava.li", "bin": "cli.js", "engines": { - "node": ">=6.12.3 <7 || >=8.9.4 <9 || >=10.0.0" + "node": ">=8.9.4 <9 || >=10.0.0" }, "scripts": { "lint": "xo", @@ -180,7 +180,15 @@ ], "rules": { "no-use-extend-native/no-use-extend-native": "off" - } + }, + "overrides": [ + { + "files": "test/**/*.js", + "rules": { + "promise/prefer-await-to-then": "off" + } + } + ] }, "nyc": { "reporter": [ diff --git a/readme.md b/readme.md index fe756e1e5..d381f501c 100644 --- a/readme.md +++ b/readme.md @@ -127,7 +127,7 @@ AVA uses [Babel 7](https://babeljs.io) so you can use the latest JavaScript synt We aim support all [finished syntax proposals](https://github.com/tc39/proposals/blob/master/finished-proposals.md), as well as all syntax from ratified JavaScript versions (e.g. ES2017). See our [`@ava/stage-4`](https://github.com/avajs/babel-preset-stage-4) preset for the currently supported proposals. -Please note that we do not add or modify built-ins. For example, if you use [`Object.entries()`](https://github.com/tc39/proposal-object-values-entries) in your tests, they will crash in Node.js 6 which does not implement this method. +Please note that we do not add or modify built-ins. For example, if you use [`Object.fromEntries()`](https://github.com/tc39/proposal-object-from-entries) in your tests, they will crash in Node.js 10 which does not implement this method. You can disable this syntax support, or otherwise customize AVA's Babel pipeline. See our [Babel recipe] for more details. diff --git a/test/api.js b/test/api.js index 22f8d5f35..a2c368f05 100644 --- a/test/api.js +++ b/test/api.js @@ -1192,27 +1192,13 @@ test('using --match with matching tests will only report those passing tests', t }); }); -function generatePassDebugTests(execArgv, expectedInspectIndex) { +function generatePassDebugTests(execArgv) { test(`pass ${execArgv.join(' ')} to fork`, t => { const api = apiCreator({testOnlyExecArgv: execArgv}); return api._computeForkExecArgv() .then(result => { t.true(result.length === execArgv.length); - if (expectedInspectIndex === -1) { - t.true(/--debug=\d+/.test(result[0])); - } else { - t.true(/--inspect=\d+/.test(result[expectedInspectIndex])); - } - }); - }); -} - -function generatePassDebugIntegrationTests(execArgv) { - test(`pass ${execArgv.join(' ')} to fork`, t => { - const api = apiCreator({testOnlyExecArgv: execArgv}); - return api.run([path.join(__dirname, 'fixture/debug-arg.js')]) - .then(runStatus => { - t.is(runStatus.stats.passedTests, 1); + t.true(/--inspect=\d+/.test(result[0])); }); }); } @@ -1227,27 +1213,11 @@ function generatePassInspectIntegrationTests(execArgv) { }); } -generatePassDebugTests(['--debug=0'], -1); -generatePassDebugTests(['--debug'], -1); - -generatePassDebugTests(['--inspect=0'], 0); -generatePassDebugTests(['--inspect'], 0); +generatePassDebugTests(['--inspect=0']); +generatePassDebugTests(['--inspect']); -// --inspect takes precedence -generatePassDebugTests(['--inspect=0', '--debug-brk'], 0); -generatePassDebugTests(['--inspect', '--debug-brk'], 0); - -// --inspect takes precedence, though --debug-brk is still passed to the worker -generatePassDebugTests(['--debug-brk', '--inspect=0'], 1); -generatePassDebugTests(['--debug-brk', '--inspect'], 1); - -if (Number(process.version.split('.')[0].slice(1)) < 8) { - generatePassDebugIntegrationTests(['--debug=0']); - generatePassDebugIntegrationTests(['--debug']); -} else { - generatePassInspectIntegrationTests(['--inspect=9229']); - generatePassInspectIntegrationTests(['--inspect']); -} +generatePassInspectIntegrationTests(['--inspect=9229']); +generatePassInspectIntegrationTests(['--inspect']); test('`esm` package support', t => { const api = apiCreator({ diff --git a/test/beautify-stack.js b/test/beautify-stack.js index 88a5e0eff..c8ef34032 100644 --- a/test/beautify-stack.js +++ b/test/beautify-stack.js @@ -57,10 +57,10 @@ test('returns empty string without any arguments', t => { t.end(); }); -test('beautify stack - removes uninteresting lines', t => { +test('beautify stack - removes uninteresting lines', async t => { try { const runner = new Runner(); - runner.runSingle({ + await runner.runSingle({ run() { fooFunc(); } diff --git a/test/fixture/babel-config/test.js b/test/fixture/babel-config/test.js index 625909b6e..f5ccda0fc 100644 --- a/test/fixture/babel-config/test.js +++ b/test/fixture/babel-config/test.js @@ -1,11 +1,11 @@ import test from '../../..'; -const one = {one: 1}; -const two = {two: 2}; - test('foo', t => { - // Using object rest/spread to ensure it transpiles on Node.js 6, since this - // is a Node.js 8 feature - const actual = {...one, ...two}; - t.deepEqual(actual, {one: 1, two: 2}); + // Using optional catch-binding to ensure it transpiles on Node.js 8, since this + // is a Node.js 10 feature + try { + throw new Error('test'); + } catch { + t.pass(); + } }); diff --git a/test/fixture/babelrc-js/test.js b/test/fixture/babelrc-js/test.js index 625909b6e..f5ccda0fc 100644 --- a/test/fixture/babelrc-js/test.js +++ b/test/fixture/babelrc-js/test.js @@ -1,11 +1,11 @@ import test from '../../..'; -const one = {one: 1}; -const two = {two: 2}; - test('foo', t => { - // Using object rest/spread to ensure it transpiles on Node.js 6, since this - // is a Node.js 8 feature - const actual = {...one, ...two}; - t.deepEqual(actual, {one: 1, two: 2}); + // Using optional catch-binding to ensure it transpiles on Node.js 8, since this + // is a Node.js 10 feature + try { + throw new Error('test'); + } catch { + t.pass(); + } }); diff --git a/test/fixture/babelrc/test.js b/test/fixture/babelrc/test.js index 625909b6e..f5ccda0fc 100644 --- a/test/fixture/babelrc/test.js +++ b/test/fixture/babelrc/test.js @@ -1,11 +1,11 @@ import test from '../../..'; -const one = {one: 1}; -const two = {two: 2}; - test('foo', t => { - // Using object rest/spread to ensure it transpiles on Node.js 6, since this - // is a Node.js 8 feature - const actual = {...one, ...two}; - t.deepEqual(actual, {one: 1, two: 2}); + // Using optional catch-binding to ensure it transpiles on Node.js 8, since this + // is a Node.js 10 feature + try { + throw new Error('test'); + } catch { + t.pass(); + } }); diff --git a/test/fixture/debug-arg.js b/test/fixture/debug-arg.js deleted file mode 100644 index 01809bd9b..000000000 --- a/test/fixture/debug-arg.js +++ /dev/null @@ -1,5 +0,0 @@ -import test from '../..'; - -test('test', t => { - t.true(process.execArgv[0].startsWith('--debug')); -}); diff --git a/test/fixture/extensions/test.foo.bar b/test/fixture/extensions/test.foo.bar index 625909b6e..0146a5186 100644 --- a/test/fixture/extensions/test.foo.bar +++ b/test/fixture/extensions/test.foo.bar @@ -4,8 +4,11 @@ const one = {one: 1}; const two = {two: 2}; test('foo', t => { - // Using object rest/spread to ensure it transpiles on Node.js 6, since this - // is a Node.js 8 feature - const actual = {...one, ...two}; - t.deepEqual(actual, {one: 1, two: 2}); + // Using optional catch-binding to ensure it transpiles on Node.js 8, since this + // is a Node.js 10 feature + try { + throw new Error('test') + } catch { + t.pass() + } }); diff --git a/test/fixture/formatting.js b/test/fixture/formatting.js index 662188828..42b8c07e3 100644 --- a/test/fixture/formatting.js +++ b/test/fixture/formatting.js @@ -332,5 +332,5 @@ test('deep structure, diff', t => { } } }; - t.deepEqual(deep, Object.assign({corge: 'grault'}, deep)); + t.deepEqual(deep, {corge: 'grault', ...deep}); }); diff --git a/test/helper/cli.js b/test/helper/cli.js index 5f6e31559..537556418 100644 --- a/test/helper/cli.js +++ b/test/helper/cli.js @@ -28,7 +28,7 @@ function execCli(args, opts, cb) { // Inserting a shim here allows us to fake a TTY. child = childProcess.spawn(process.execPath, ['-r', ttySimulator, cliPath].concat(args), { cwd: dirname, - env: Object.assign({CI: '1'}, env), // Force CI to ensure the correct reporter is selected + env: {CI: '1', ...env}, // Force CI to ensure the correct reporter is selected // env, stdio: [null, 'pipe', 'pipe'] }); diff --git a/test/helper/report.js b/test/helper/report.js index 9bf5e5114..cd49855f5 100644 --- a/test/helper/report.js +++ b/test/helper/report.js @@ -12,11 +12,12 @@ const createApi = options => { if (!_Api) { _Api = proxyquire('../../lib/api', { './fork': proxyquire('../../lib/fork', { - child_process: Object.assign({}, childProcess, { // eslint-disable-line camelcase + child_process: { // eslint-disable-line camelcase + ...childProcess, fork(filename, argv, options) { return childProcess.fork(path.join(__dirname, 'report-worker.js'), argv, options); } - }) + } }) }); } @@ -24,15 +25,7 @@ const createApi = options => { return new _Api(options); }; -// At least in Appveyor with Node.js 6, IPC can overtake stdout/stderr -let hasReliableStdIO = true; -exports.captureStdIOReliability = () => { - if (process.platform === 'win32' && parseInt(process.versions.node, 10) < 8) { - hasReliableStdIO = false; - } -}; - -exports.assert = (t, logFile, buffer, stripOptions) => { +exports.assert = (t, logFile, buffer) => { let existing = null; try { existing = fs.readFileSync(logFile); @@ -43,14 +36,7 @@ exports.assert = (t, logFile, buffer, stripOptions) => { existing = buffer; } - let expected = existing.toString('utf8'); - // At least in Appveyor with Node.js 6, IPC can overtake stdout/stderr. This - // causes the reporter to emit in a different order, resulting in a test - // failure. "Fix" by not asserting on the stdout/stderr reporting at all. - if (stripOptions.stripStdIO && !hasReliableStdIO) { - expected = expected.replace(/(---tty-stream-chunk-separator\n)(stderr|stdout)\n/g, stripOptions.alsoStripSeparator ? '' : '$1'); - } - + const expected = existing.toString('utf8'); const actual = buffer.toString('utf8'); if (actual === expected) { t.pass(); @@ -69,16 +55,6 @@ exports.sanitizers = { posix: str => replaceString(str, '\\', '/'), slow: str => str.replace(/(slow.+?)\(\d+m?s\)/g, '$1 (000ms)'), timeout: str => replaceString(str, 'Timeout._onTimeout', 'Timeout.setTimeout'), - // At least in Appveyor with Node.js 6, IPC can overtake stdout/stderr. This - // causes the reporter to emit in a different order, resulting in a test - // failure. "Fix" by not asserting on the stdout/stderr reporting at all. - unreliableProcessIO(str) { - if (hasReliableStdIO) { - return str; - } - - return str === 'stdout\n' || str === 'stderr\n' ? '' : str; - }, version: str => replaceString(str, `v${pkg.version}`, 'v1.0.0-beta.5.1') }; diff --git a/test/reporters/mini.js b/test/reporters/mini.js index 9e29c48c7..86cf95bfe 100644 --- a/test/reporters/mini.js +++ b/test/reporters/mini.js @@ -1,5 +1,4 @@ 'use strict'; -require('../helper/report').captureStdIOReliability(); require('../helper/fix-reporter-env')(); // Excessive writes occur in Node.js 11. These don't have a visual impact but prevent the integration tests from passing. @@ -20,7 +19,7 @@ const run = (type, sanitizers = []) => t => { const tty = new TTYStream({ columns: 200, - sanitizers: [...sanitizers, report.sanitizers.cwd, report.sanitizers.experimentalWarning, report.sanitizers.posix, report.sanitizers.unreliableProcessIO, report.sanitizers.version] + sanitizers: [...sanitizers, report.sanitizers.cwd, report.sanitizers.experimentalWarning, report.sanitizers.posix, report.sanitizers.version] }); const reporter = new MiniReporter({ spinner: { @@ -37,7 +36,7 @@ const run = (type, sanitizers = []) => t => { tty.end(); return tty.asBuffer(); }) - .then(buffer => report.assert(t, logFile, buffer, {stripStdIO: true, alsoStripSeparator: false})) + .then(buffer => report.assert(t, logFile, buffer)) .catch(t.threw); }; diff --git a/test/reporters/tap.js b/test/reporters/tap.js index dc14735c8..c36ba2a1e 100644 --- a/test/reporters/tap.js +++ b/test/reporters/tap.js @@ -1,5 +1,4 @@ 'use strict'; -require('../helper/report').captureStdIOReliability(); require('../helper/fix-reporter-env')(); const path = require('path'); @@ -15,7 +14,7 @@ const run = (type, sanitizers = []) => t => { const tty = new TTYStream({ columns: 200, - sanitizers: [...sanitizers, report.sanitizers.cwd, report.sanitizers.experimentalWarning, report.sanitizers.posix, report.sanitizers.timeout, report.sanitizers.unreliableProcessIO] + sanitizers: [...sanitizers, report.sanitizers.cwd, report.sanitizers.experimentalWarning, report.sanitizers.posix, report.sanitizers.timeout] }); const reporter = new TapReporter({ reportStream: tty, @@ -26,7 +25,7 @@ const run = (type, sanitizers = []) => t => { tty.end(); return tty.asBuffer(); }) - .then(buffer => report.assert(t, logFile, buffer, {stripStdIO: true, alsoStripSeparator: true})) + .then(buffer => report.assert(t, logFile, buffer)) .catch(t.threw); }; diff --git a/test/reporters/verbose.js b/test/reporters/verbose.js index 2d7ee73ec..9146b2d00 100644 --- a/test/reporters/verbose.js +++ b/test/reporters/verbose.js @@ -1,6 +1,4 @@ 'use strict'; -require('../helper/report').captureStdIOReliability(); - const path = require('path'); const {test} = require('tap'); const {restoreClock} = require('../helper/fix-reporter-env')(); @@ -15,7 +13,7 @@ const run = (type, sanitizers = []) => t => { const tty = new TTYStream({ columns: 200, - sanitizers: [...sanitizers, report.sanitizers.cwd, report.sanitizers.experimentalWarning, report.sanitizers.posix, report.sanitizers.slow, report.sanitizers.unreliableProcessIO, report.sanitizers.version] + sanitizers: [...sanitizers, report.sanitizers.cwd, report.sanitizers.experimentalWarning, report.sanitizers.posix, report.sanitizers.slow, report.sanitizers.version] }); const reporter = new VerboseReporter({ reportStream: tty, @@ -27,7 +25,7 @@ const run = (type, sanitizers = []) => t => { tty.end(); return tty.asBuffer(); }) - .then(buffer => report.assert(t, logFile, buffer, {stripStdIO: true, alsoStripSeparator: true})) + .then(buffer => report.assert(t, logFile, buffer)) .catch(t.threw); }; diff --git a/test/watcher.js b/test/watcher.js index a0e06d7f5..6423ba9d6 100644 --- a/test/watcher.js +++ b/test/watcher.js @@ -307,10 +307,11 @@ group('chokidar', (beforeEach, test, group) => { return debounce().then(() => { t.ok(api.run.calledTwice); // No explicit files are provided - t.strictDeepEqual(api.run.secondCall.args, [files, Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [files, { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); // Finish is only called after the run promise fulfils t.ok(reporter.endRun.calledOnce); @@ -348,18 +349,20 @@ group('chokidar', (beforeEach, test, group) => { api.run.returns(Promise.resolve(resetRunStatus())); change(); return debounce().then(() => { - t.strictDeepEqual(api.run.secondCall.args, [files, Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [files, { + ...defaultApiOptions, clearLogOnNextRun: false, runVector: 2 - })]); + }]); change(); return debounce(); }).then(() => { - t.strictDeepEqual(api.run.thirdCall.args, [files, Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.thirdCall.args, [files, { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 3 - })]); + }]); }); }); }); @@ -484,10 +487,11 @@ group('chokidar', (beforeEach, test, group) => { return debounce().then(() => { t.ok(api.run.calledTwice); // The `test.js` file is provided - t.strictDeepEqual(api.run.secondCall.args, [['test.js'], Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [['test.js'], { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); // The endRun method is only called after the run promise fulfills t.ok(reporter.endRun.calledOnce); @@ -511,10 +515,11 @@ group('chokidar', (beforeEach, test, group) => { return debounce(2).then(() => { t.ok(api.run.calledTwice); // The test files are provided - t.strictDeepEqual(api.run.secondCall.args, [['test-one.js', 'test-two.js'], Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [['test-one.js', 'test-two.js'], { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); @@ -528,10 +533,11 @@ group('chokidar', (beforeEach, test, group) => { return debounce(2).then(() => { t.ok(api.run.calledTwice); // No explicit files are provided - t.strictDeepEqual(api.run.secondCall.args, [files, Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [files, { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); @@ -557,10 +563,11 @@ group('chokidar', (beforeEach, test, group) => { add('foo-baz.js'); return debounce(2).then(() => { t.ok(api.run.calledTwice); - t.strictDeepEqual(api.run.secondCall.args, [['foo-bar.js', 'foo-baz.js'], Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [['foo-bar.js', 'foo-baz.js'], { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); @@ -587,10 +594,11 @@ group('chokidar', (beforeEach, test, group) => { t.ok(api.run.calledTwice); // `foo-bar.js` is excluded from being a test file, thus the initial tests // are run - t.strictDeepEqual(api.run.secondCall.args, [files, Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [files, { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); @@ -605,10 +613,11 @@ group('chokidar', (beforeEach, test, group) => { return debounce(2).then(() => { t.ok(api.run.calledTwice); // `foo.bar` cannot be a test file, thus the initial tests are run - t.strictDeepEqual(api.run.secondCall.args, [files, Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [files, { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); @@ -623,10 +632,11 @@ group('chokidar', (beforeEach, test, group) => { return debounce(2).then(() => { t.ok(api.run.calledTwice); // `_foo.bar` cannot be a test file, thus the initial tests are run - t.strictDeepEqual(api.run.secondCall.args, [files, Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [files, { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); @@ -648,10 +658,11 @@ group('chokidar', (beforeEach, test, group) => { path.join('dir', 'nested', 'test.js'), path.join('another-dir', 'nested', 'deeper', 'test.js') ], - Object.assign({}, defaultApiOptions, { + { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - }) + } ]); }); }); @@ -678,10 +689,11 @@ group('chokidar', (beforeEach, test, group) => { t.ok(api.run.calledTwice); // `dir/exclude/foo.js` is excluded from being a test file, thus the initial // tests are run - t.strictDeepEqual(api.run.secondCall.args, [files, Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [files, { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); @@ -694,23 +706,19 @@ group('chokidar', (beforeEach, test, group) => { stdin.write(`${input}\n`); return delay().then(() => { t.ok(api.run.calledTwice); - t.strictDeepEqual(api.run.secondCall.args, [files, Object.assign({}, defaultApiOptions, { - runVector: 2 - })]); + t.strictDeepEqual(api.run.secondCall.args, [files, {...defaultApiOptions, runVector: 2}]); stdin.write(`\t${input} \n`); return delay(); }).then(() => { t.ok(api.run.calledThrice); - t.strictDeepEqual(api.run.thirdCall.args, [files, Object.assign({}, defaultApiOptions, { - runVector: 3 - })]); + t.strictDeepEqual(api.run.thirdCall.args, [files, {...defaultApiOptions, runVector: 3}]); }); }); } test('reruns previous tests and update snapshots when "u" is entered on stdin', t => { - const options = Object.assign({}, defaultApiOptions, {updateSnapshots: true}); + const options = {...defaultApiOptions, updateSnapshots: true}; const previousFiles = ['test.js']; t.plan(4); api.run.returns(Promise.resolve(runStatus)); @@ -719,17 +727,13 @@ group('chokidar', (beforeEach, test, group) => { stdin.write('u\n'); return delay().then(() => { t.ok(api.run.calledTwice); - t.strictDeepEqual(api.run.secondCall.args, [previousFiles, Object.assign({}, options, { - runVector: 2 - })]); + t.strictDeepEqual(api.run.secondCall.args, [previousFiles, {...options, runVector: 2}]); stdin.write('\tu \n'); return delay(); }).then(() => { t.ok(api.run.calledThrice); - t.strictDeepEqual(api.run.thirdCall.args, [previousFiles, Object.assign({}, options, { - runVector: 3 - })]); + t.strictDeepEqual(api.run.thirdCall.args, [previousFiles, {...options, runVector: 3}]); }); }); @@ -742,11 +746,12 @@ group('chokidar', (beforeEach, test, group) => { stdin.write(`${input}\n`); return delay().then(() => { t.ok(api.run.calledTwice); - t.strictDeepEqual(api.run.secondCall.args, [files, Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [files, { + ...defaultApiOptions, clearLogOnNextRun: false, runVector: 2, updateSnapshots: input === 'u' - })]); + }]); }); }); @@ -959,10 +964,11 @@ group('chokidar', (beforeEach, test, group) => { change('dep-1.js'); return debounce().then(() => { t.ok(api.run.calledTwice); - t.strictDeepEqual(api.run.secondCall.args, [[path.join('test', '1.js')], Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [[path.join('test', '1.js')], { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); @@ -973,10 +979,11 @@ group('chokidar', (beforeEach, test, group) => { change('cannot-be-mapped.js'); return debounce().then(() => { t.ok(api.run.calledTwice); - t.strictDeepEqual(api.run.secondCall.args, [files, Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [files, { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); @@ -990,10 +997,11 @@ group('chokidar', (beforeEach, test, group) => { t.ok(api.run.calledTwice); t.strictDeepEqual(api.run.secondCall.args, [ [path.join('test', '2.js'), path.join('test', '1.js')], - Object.assign({}, defaultApiOptions, { + { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - }) + } ]); }); }); @@ -1006,10 +1014,11 @@ group('chokidar', (beforeEach, test, group) => { change('dep-1.js'); return debounce(2).then(() => { t.ok(api.run.calledTwice); - t.strictDeepEqual(api.run.secondCall.args, [[path.join('test', '1.js')], Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [[path.join('test', '1.js')], { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); @@ -1021,10 +1030,11 @@ group('chokidar', (beforeEach, test, group) => { change('dep-3.js'); return debounce(2).then(() => { t.ok(api.run.calledTwice); - t.strictDeepEqual(api.run.secondCall.args, [[path.join('test', '2.js')], Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [[path.join('test', '2.js')], { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); @@ -1036,10 +1046,11 @@ group('chokidar', (beforeEach, test, group) => { change('dep-4.js'); return debounce().then(() => { t.ok(api.run.calledTwice); - t.strictDeepEqual(api.run.secondCall.args, [[path.join('test', '1.js')], Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [[path.join('test', '1.js')], { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); @@ -1065,10 +1076,11 @@ group('chokidar', (beforeEach, test, group) => { t.ok(api.run.calledTwice); // Expect all tests to be rerun since `dep-2.js` is not a tracked // dependency - t.strictDeepEqual(api.run.secondCall.args, [files, Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [files, { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); }); @@ -1086,10 +1098,11 @@ group('chokidar', (beforeEach, test, group) => { api.run.returns(Promise.resolve(runStatus)); return debounce(3).then(() => { t.ok(api.run.calledTwice); - t.strictDeepEqual(api.run.secondCall.args, [[path.join('test', '1.js')], Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [[path.join('test', '1.js')], { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); change('foo.bar'); return debounce(); @@ -1097,10 +1110,11 @@ group('chokidar', (beforeEach, test, group) => { t.ok(api.run.calledThrice); // Expect all tests to be rerun since `foo.bar` is not a tracked // dependency - t.strictDeepEqual(api.run.thirdCall.args, [files, Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.thirdCall.args, [files, { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 3 - })]); + }]); }); }); @@ -1134,10 +1148,11 @@ group('chokidar', (beforeEach, test, group) => { t.ok(api.run.calledTwice); // Since the excluded files are not tracked as a dependency, all tests // are expected to be rerun - t.strictDeepEqual(api.run.secondCall.args, [files, Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [files, { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); @@ -1151,10 +1166,11 @@ group('chokidar', (beforeEach, test, group) => { return debounce(1).then(() => { t.ok(api.run.calledTwice); - t.strictDeepEqual(api.run.secondCall.args, [[path.join('test', '1.js')], Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [[path.join('test', '1.js')], { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); @@ -1177,19 +1193,21 @@ group('chokidar', (beforeEach, test, group) => { // wouldn't even be picked up. However this lets us test dependency // tracking without directly inspecting the internal state of the // watcher. - t.strictDeepEqual(api.run.secondCall.args, [files, Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [files, { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); change('..foo.js'); return debounce(); }).then(() => { t.ok(api.run.calledThrice); - t.strictDeepEqual(api.run.thirdCall.args, [[path.join('test', '2.js')], Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.thirdCall.args, [[path.join('test', '2.js')], { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 3 - })]); + }]); }); }); @@ -1301,7 +1319,7 @@ group('chokidar', (beforeEach, test, group) => { }; test('changed test files (none of which previously contained .only) are run in exclusive mode', t => { - const options = Object.assign({}, defaultApiOptions, {runOnlyExclusive: true}); + const options = {...defaultApiOptions, runOnlyExclusive: true}; t.plan(2); seed(); @@ -1309,15 +1327,16 @@ group('chokidar', (beforeEach, test, group) => { change(t4); return debounce(2).then(() => { t.ok(api.run.calledTwice); - t.strictDeepEqual(api.run.secondCall.args, [[t1, t2, t3, t4], Object.assign({}, options, { + t.strictDeepEqual(api.run.secondCall.args, [[t1, t2, t3, t4], { + ...options, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); test('changed test files (comprising some, but not all, files that previously contained .only) are run in exclusive mode', t => { - const options = Object.assign({}, defaultApiOptions, {runOnlyExclusive: true}); + const options = {...defaultApiOptions, runOnlyExclusive: true}; t.plan(2); seed(); @@ -1325,10 +1344,11 @@ group('chokidar', (beforeEach, test, group) => { change(t4); return debounce(2).then(() => { t.ok(api.run.calledTwice); - t.strictDeepEqual(api.run.secondCall.args, [[t1, t2, t4], Object.assign({}, options, { + t.strictDeepEqual(api.run.secondCall.args, [[t1, t2, t4], { + ...options, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); @@ -1340,10 +1360,11 @@ group('chokidar', (beforeEach, test, group) => { change(t2); return debounce(2).then(() => { t.ok(api.run.calledTwice); - t.strictDeepEqual(api.run.secondCall.args, [[t1, t2], Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [[t1, t2], { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); @@ -1358,10 +1379,11 @@ group('chokidar', (beforeEach, test, group) => { change(t4); return debounce(2).then(() => { t.ok(api.run.calledTwice); - t.strictDeepEqual(api.run.secondCall.args, [[t3, t4], Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [[t3, t4], { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); @@ -1375,10 +1397,11 @@ group('chokidar', (beforeEach, test, group) => { change(t4); return debounce(4).then(() => { t.ok(api.run.calledTwice); - t.strictDeepEqual(api.run.secondCall.args, [[t3, t4], Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [[t3, t4], { + ...defaultApiOptions, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); }); @@ -1485,11 +1508,12 @@ group('chokidar', (beforeEach, test, group) => { return rerun(other).then(() => { t.ok(api.run.calledTwice); - t.strictDeepEqual(api.run.secondCall.args, [[other], Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [[other], { + ...defaultApiOptions, previousFailures: 2, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); @@ -1514,11 +1538,12 @@ group('chokidar', (beforeEach, test, group) => { return rerun(first).then(() => { t.ok(api.run.calledTwice); - t.strictDeepEqual(api.run.secondCall.args, [[first], Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [[first], { + ...defaultApiOptions, previousFailures: 1, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); @@ -1543,11 +1568,12 @@ group('chokidar', (beforeEach, test, group) => { return rerun(same).then(() => { t.ok(api.run.calledTwice); - t.strictDeepEqual(api.run.secondCall.args, [[same], Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [[same], { + ...defaultApiOptions, previousFailures: 0, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); @@ -1576,11 +1602,12 @@ group('chokidar', (beforeEach, test, group) => { return debounce().then(() => rerun(other)).then(() => { t.ok(api.run.calledTwice); - t.strictDeepEqual(api.run.secondCall.args, [[other], Object.assign({}, defaultApiOptions, { + t.strictDeepEqual(api.run.secondCall.args, [[other], { + ...defaultApiOptions, previousFailures: 0, clearLogOnNextRun: true, runVector: 2 - })]); + }]); }); }); });