From f6305c04786da149ca79fef1978b95a46c16f761 Mon Sep 17 00:00:00 2001 From: Frederik Ring Date: Thu, 16 Sep 2021 18:02:41 +0200 Subject: [PATCH 1/6] Allow to read bundled tests from stdin --- cli/index.js | 10 +++++++--- cli/test/jsdom.integration.js | 22 ++++++++++++++++++++++ cli/test/playwright.integration.js | 26 ++++++++++++++++++++++++++ cli/test/puppeteer.integration.js | 22 ++++++++++++++++++++++ mochify/lib/load-config.js | 4 +++- mochify/lib/resolve-bundle.js | 21 +++++++++++++++++++++ mochify/lib/resolve-spec.js | 4 ++++ 7 files changed, 105 insertions(+), 4 deletions(-) diff --git a/cli/index.js b/cli/index.js index 5c8d59f8..8e2d96e6 100755 --- a/cli/index.js +++ b/cli/index.js @@ -88,14 +88,18 @@ if (opts['server-option']) { (async () => { if (opts._.length) { - opts.spec = opts._; + if (opts._[0] === '-') { + opts.spec = process.stdin; + } else { + opts.spec = opts._; + } } delete opts._; try { const { exit_code } = await mochify(opts); - process.exitCode = exit_code; + process.exitCode = exit_code; // eslint-disable-line require-atomic-updates } catch (e) { console.error(e.stack); - process.exitCode = 1; + process.exitCode = 1; // eslint-disable-line require-atomic-updates } })(); diff --git a/cli/test/jsdom.integration.js b/cli/test/jsdom.integration.js index a110f10b..2ef67181 100644 --- a/cli/test/jsdom.integration.js +++ b/cli/test/jsdom.integration.js @@ -1,5 +1,6 @@ 'use strict'; +const fs = require('fs'); const path = require('path'); const { assert } = require('@sinonjs/referee-sinon'); const execa = require('execa'); @@ -28,6 +29,27 @@ describe('jsdom', () => { assert.equals(json.tests[0].fullTitle, 'test passes'); }); + it('reads from stdin', async () => { + let result; + try { + const cp = execa('../../index.js', ['--driver', 'jsdom', '-'], { + cwd: path.join(__dirname, 'fixture') + }); + const fixture = fs.createReadStream( + path.resolve(__dirname, './fixture/passes.js') + ); + fixture.pipe(cp.stdin); + result = await cp; + } catch (err) { + result = err; + } + + assert.isFalse(result.failed); + const json = JSON.parse(result.stdout); + assert.equals(json.tests.length, 1); + assert.equals(json.tests[0].fullTitle, 'test passes'); + }); + it('fails', async () => { const result = await run('fails.js'); diff --git a/cli/test/playwright.integration.js b/cli/test/playwright.integration.js index 7c19ec54..d48292d9 100644 --- a/cli/test/playwright.integration.js +++ b/cli/test/playwright.integration.js @@ -1,5 +1,6 @@ 'use strict'; +const fs = require('fs'); const path = require('path'); const { assert } = require('@sinonjs/referee-sinon'); const execa = require('execa'); @@ -35,6 +36,31 @@ describe('playwright', () => { assert.equals(json.tests[0].fullTitle, 'test passes'); }); + it('reads from stdin', async () => { + let result; + try { + const cp = execa( + '../../index.js', + ['--driver', 'playwright', '--driver-option.engine', 'firefox', '-'], + { + cwd: path.join(__dirname, 'fixture') + } + ); + const fixture = fs.createReadStream( + path.resolve(__dirname, './fixture/passes.js') + ); + fixture.pipe(cp.stdin); + result = await cp; + } catch (err) { + result = err; + } + + assert.isFalse(result.failed); + const json = JSON.parse(result.stdout); + assert.equals(json.tests.length, 1); + assert.equals(json.tests[0].fullTitle, 'test passes'); + }); + it('fails', async () => { const result = await run('fails.js'); diff --git a/cli/test/puppeteer.integration.js b/cli/test/puppeteer.integration.js index f917b839..1a396d68 100644 --- a/cli/test/puppeteer.integration.js +++ b/cli/test/puppeteer.integration.js @@ -1,5 +1,6 @@ 'use strict'; +const fs = require('fs'); const path = require('path'); const { assert } = require('@sinonjs/referee-sinon'); const execa = require('execa'); @@ -28,6 +29,27 @@ describe('puppeteer', () => { assert.equals(json.tests[0].fullTitle, 'test passes'); }); + it('reads from stdin', async () => { + let result; + try { + const cp = execa('../../index.js', ['--driver', 'puppeteer', '-'], { + cwd: path.join(__dirname, 'fixture') + }); + const fixture = fs.createReadStream( + path.resolve(__dirname, './fixture/passes.js') + ); + fixture.pipe(cp.stdin); + result = await cp; + } catch (err) { + result = err; + } + + assert.isFalse(result.failed); + const json = JSON.parse(result.stdout); + assert.equals(json.tests.length, 1); + assert.equals(json.tests[0].fullTitle, 'test passes'); + }); + it('fails', async () => { const result = await run('fails.js'); diff --git a/mochify/lib/load-config.js b/mochify/lib/load-config.js index 7cc3b907..1b39890a 100644 --- a/mochify/lib/load-config.js +++ b/mochify/lib/load-config.js @@ -22,7 +22,9 @@ async function loadConfig(options) { async function mergeWithDefault(default_config_promise, config) { const default_config = await default_config_promise; if (default_config) { - return deepmerge(default_config.config, config); + return deepmerge(default_config.config, config, { + clone: false + }); } return config; } diff --git a/mochify/lib/resolve-bundle.js b/mochify/lib/resolve-bundle.js index f5777c74..b99fef35 100644 --- a/mochify/lib/resolve-bundle.js +++ b/mochify/lib/resolve-bundle.js @@ -7,6 +7,27 @@ const { parseArgsStringToArgv } = require('string-argv'); exports.resolveBundle = resolveBundle; async function resolveBundle(command, files) { + if (command && files) { + throw new Error( + 'Cannot use a stream as input when a bundle command is given.' + ); + } + + if (typeof files === 'object' && typeof files.pipe === 'function') { + return new Promise((resolve, reject) => { + let buf; + files.on('data', (data) => { + buf += data; + }); + files.on('error', (err) => { + reject(err); + }); + files.on('end', () => { + resolve(buf); + }); + }); + } + if (!command) { return concatFiles(files); } diff --git a/mochify/lib/resolve-spec.js b/mochify/lib/resolve-spec.js index f71581a8..e3dd0a04 100644 --- a/mochify/lib/resolve-spec.js +++ b/mochify/lib/resolve-spec.js @@ -6,6 +6,10 @@ const glob = promisify(require('glob')); exports.resolveSpec = resolveSpec; async function resolveSpec(spec = 'test/**/*.js') { + if (typeof spec === 'object' && typeof spec.pipe === 'function') { + return spec; + } + const patterns = Array.isArray(spec) ? spec : [spec]; const matches = await Promise.all(patterns.map((pattern) => glob(pattern))); return matches.reduce((all, match) => all.concat(match), []); From 36777d0102ac3f696cb113b12883bdbb3574c57b Mon Sep 17 00:00:00 2001 From: Frederik Ring Date: Thu, 16 Sep 2021 18:18:58 +0200 Subject: [PATCH 2/6] Validate config right after merging --- mochify/lib/load-config.js | 25 +++++++++++++++++++++++-- mochify/lib/load-config.test.js | 10 ++++++++++ mochify/lib/resolve-bundle.js | 28 ++++++++++------------------ mochify/lib/resolve-spec.test.js | 7 +++++++ 4 files changed, 50 insertions(+), 20 deletions(-) diff --git a/mochify/lib/load-config.js b/mochify/lib/load-config.js index 1b39890a..ae4d913a 100644 --- a/mochify/lib/load-config.js +++ b/mochify/lib/load-config.js @@ -10,13 +10,20 @@ async function loadConfig(options) { const default_config_promise = explorer.search(); + let merged; if (options.config) { const specified = await explorer.load(options.config); const config = Object.assign(specified.config, options); - return mergeWithDefault(default_config_promise, config); + merged = await mergeWithDefault(default_config_promise, config); + } else { + merged = await mergeWithDefault(default_config_promise, options); } - return mergeWithDefault(default_config_promise, options); + const validation_error = validate(merged); + if (validation_error) { + throw validation_error; + } + return merged; } async function mergeWithDefault(default_config_promise, config) { @@ -28,3 +35,17 @@ async function mergeWithDefault(default_config_promise, config) { } return config; } + +function validate(config) { + if (config.esm && config.bundle) { + return new Error('`esm` cannot be used in conjunction with `bundle`'); + } + if ( + config.bundle && + typeof config.spec === 'object' && + typeof config.spec.pipe === 'function' + ) { + return new Error('`bundle` cannot be used when `spec` is a stream.'); + } + return null; +} diff --git a/mochify/lib/load-config.test.js b/mochify/lib/load-config.test.js index 21e34580..d9af925a 100644 --- a/mochify/lib/load-config.test.js +++ b/mochify/lib/load-config.test.js @@ -154,4 +154,14 @@ describe('mochify/lib/load-config', () => { reporter: 'nyan' }); }); + + it('rejects on invalid configuration', async () => { + setDefaultConfig({ esm: true }); + setSpecifiedConfig({ bundle: 'browserify' }); + const loadConfig = requireLoadConfig(); + + const promise = loadConfig({ config: 'some.config.js' }); + + await assert.rejects(promise); + }); }); diff --git a/mochify/lib/resolve-bundle.js b/mochify/lib/resolve-bundle.js index b99fef35..d8767771 100644 --- a/mochify/lib/resolve-bundle.js +++ b/mochify/lib/resolve-bundle.js @@ -7,25 +7,8 @@ const { parseArgsStringToArgv } = require('string-argv'); exports.resolveBundle = resolveBundle; async function resolveBundle(command, files) { - if (command && files) { - throw new Error( - 'Cannot use a stream as input when a bundle command is given.' - ); - } - if (typeof files === 'object' && typeof files.pipe === 'function') { - return new Promise((resolve, reject) => { - let buf; - files.on('data', (data) => { - buf += data; - }); - files.on('error', (err) => { - reject(err); - }); - files.on('end', () => { - resolve(buf); - }); - }); + return bufferStream(files); } if (!command) { @@ -49,3 +32,12 @@ async function concatFiles(files) { const buffers = await Promise.all(files.map((file) => fs.readFile(file))); return Buffer.concat(buffers).toString('utf8'); } + +function bufferStream(stream) { + return new Promise((resolve, reject) => { + const bs = []; + stream.on('data', (chunk) => bs.push(chunk)); + stream.on('error', (err) => reject(err)); + stream.on('end', () => resolve(Buffer.concat(bs).toString())); + }); +} diff --git a/mochify/lib/resolve-spec.test.js b/mochify/lib/resolve-spec.test.js index 6c2c0224..4d90d305 100644 --- a/mochify/lib/resolve-spec.test.js +++ b/mochify/lib/resolve-spec.test.js @@ -1,5 +1,6 @@ 'use strict'; +const fs = require('fs'); const proxyquire = require('proxyquire'); const { assert, sinon } = require('@sinonjs/referee-sinon'); @@ -75,4 +76,10 @@ describe('mochify/lib/resolve-spec', () => { await assert.rejects(promise, error); }); + + it('passes through streams', async () => { + const stream = fs.createReadStream(__filename); + const promise = resolveSpec(stream); + await assert.resolves(promise, stream); + }); }); From 6d939ec28dfbc019364ab9006b1550938cfa374e Mon Sep 17 00:00:00 2001 From: Frederik Ring Date: Fri, 17 Sep 2021 12:16:01 +0200 Subject: [PATCH 3/6] Factor out config validation into own module --- cli/index.js | 31 ++++++++++++------------- mochify/index.js | 6 +++++ mochify/lib/load-config.js | 25 ++------------------ mochify/lib/load-config.test.js | 10 -------- mochify/lib/validate-config.js | 17 ++++++++++++++ mochify/lib/validate-config.test.js | 36 +++++++++++++++++++++++++++++ 6 files changed, 76 insertions(+), 49 deletions(-) create mode 100644 mochify/lib/validate-config.js create mode 100644 mochify/lib/validate-config.test.js diff --git a/cli/index.js b/cli/index.js index 8e2d96e6..e298947c 100755 --- a/cli/index.js +++ b/cli/index.js @@ -86,20 +86,19 @@ if (opts['server-option']) { opts.server_options = opts['server-option']; } -(async () => { - if (opts._.length) { - if (opts._[0] === '-') { - opts.spec = process.stdin; - } else { - opts.spec = opts._; - } +if (opts._.length) { + if (opts._[0] === '-') { + opts.spec = process.stdin; + } else { + opts.spec = opts._; } - delete opts._; - try { - const { exit_code } = await mochify(opts); - process.exitCode = exit_code; // eslint-disable-line require-atomic-updates - } catch (e) { - console.error(e.stack); - process.exitCode = 1; // eslint-disable-line require-atomic-updates - } -})(); +} +delete opts._; +mochify(opts) + .catch((err) => { + console.error(err.stack); + return { exit_code: 1 }; + }) + .then(({ exit_code }) => { + process.exitCode = exit_code; + }); diff --git a/mochify/index.js b/mochify/index.js index b787463b..cf4926da 100644 --- a/mochify/index.js +++ b/mochify/index.js @@ -2,6 +2,7 @@ const { readFile } = require('fs').promises; const { loadConfig } = require('./lib/load-config'); +const { validateConfig } = require('./lib/validate-config'); const { setupClient } = require('./lib/setup-client'); const { createMochaRunner } = require('./lib/mocha-runner'); const { resolveBundle } = require('./lib/resolve-bundle'); @@ -14,6 +15,11 @@ exports.mochify = mochify; async function mochify(options = {}) { const config = await loadConfig(options); + const validation_error = validateConfig(config); + if (validation_error) { + throw validation_error; + } + // Create runner early to verify the reporter exists: const mocha_runner = createMochaRunner(config.reporter || 'spec'); const { mochifyDriver } = resolveMochifyDriver(config.driver); diff --git a/mochify/lib/load-config.js b/mochify/lib/load-config.js index ae4d913a..1b39890a 100644 --- a/mochify/lib/load-config.js +++ b/mochify/lib/load-config.js @@ -10,20 +10,13 @@ async function loadConfig(options) { const default_config_promise = explorer.search(); - let merged; if (options.config) { const specified = await explorer.load(options.config); const config = Object.assign(specified.config, options); - merged = await mergeWithDefault(default_config_promise, config); - } else { - merged = await mergeWithDefault(default_config_promise, options); + return mergeWithDefault(default_config_promise, config); } - const validation_error = validate(merged); - if (validation_error) { - throw validation_error; - } - return merged; + return mergeWithDefault(default_config_promise, options); } async function mergeWithDefault(default_config_promise, config) { @@ -35,17 +28,3 @@ async function mergeWithDefault(default_config_promise, config) { } return config; } - -function validate(config) { - if (config.esm && config.bundle) { - return new Error('`esm` cannot be used in conjunction with `bundle`'); - } - if ( - config.bundle && - typeof config.spec === 'object' && - typeof config.spec.pipe === 'function' - ) { - return new Error('`bundle` cannot be used when `spec` is a stream.'); - } - return null; -} diff --git a/mochify/lib/load-config.test.js b/mochify/lib/load-config.test.js index d9af925a..21e34580 100644 --- a/mochify/lib/load-config.test.js +++ b/mochify/lib/load-config.test.js @@ -154,14 +154,4 @@ describe('mochify/lib/load-config', () => { reporter: 'nyan' }); }); - - it('rejects on invalid configuration', async () => { - setDefaultConfig({ esm: true }); - setSpecifiedConfig({ bundle: 'browserify' }); - const loadConfig = requireLoadConfig(); - - const promise = loadConfig({ config: 'some.config.js' }); - - await assert.rejects(promise); - }); }); diff --git a/mochify/lib/validate-config.js b/mochify/lib/validate-config.js new file mode 100644 index 00000000..9a69e4c8 --- /dev/null +++ b/mochify/lib/validate-config.js @@ -0,0 +1,17 @@ +'use strict'; + +exports.validateConfig = validateConfig; + +function validateConfig(config) { + if (config.esm && config.bundle) { + return new Error('`esm` cannot be used in conjunction with `bundle`'); + } + if ( + config.bundle && + typeof config.spec === 'object' && + typeof config.spec.pipe === 'function' + ) { + return new Error('`bundle` cannot be used when `spec` is a stream.'); + } + return null; +} diff --git a/mochify/lib/validate-config.test.js b/mochify/lib/validate-config.test.js new file mode 100644 index 00000000..2957363a --- /dev/null +++ b/mochify/lib/validate-config.test.js @@ -0,0 +1,36 @@ +'use strict'; + +const fs = require('fs'); +const { assert } = require('@sinonjs/referee-sinon'); +const { validateConfig } = require('./validate-config'); + +describe('mochify/lib/validate-config', () => { + it('returns an error when esm and bundle are given', () => { + assert.isError( + validateConfig({ esm: true, bundle: 'browserify', spec: './test.js' }) + ); + }); + + it('returns an error when bundle and a stream spec are given', () => { + assert.isError( + validateConfig({ + bundle: 'browserify', + spec: fs.createReadStream(__filename) + }) + ); + }); + + it('returns null on an empty config', () => { + assert.isNull(validateConfig({})); + }); + + it('returns null on a valid config', () => { + assert.isNull( + validateConfig({ + bundle: 'browserify -t babelify', + spec: './test.js', + driver: 'puppeteer' + }) + ); + }); +}); From 1a08cba558db58012594421aae5a7dbb31a4e4a5 Mon Sep 17 00:00:00 2001 From: Frederik Ring Date: Fri, 17 Sep 2021 12:36:22 +0200 Subject: [PATCH 4/6] Improve naming around ambiguous items --- cli/README.md | 18 ++++++++++++++++++ cli/index.js | 4 ++++ mochify/index.js | 6 +++--- mochify/lib/resolve-bundle.js | 19 +++++++++++-------- 4 files changed, 36 insertions(+), 11 deletions(-) diff --git a/cli/README.md b/cli/README.md index 5df12889..c3b99e52 100644 --- a/cli/README.md +++ b/cli/README.md @@ -64,3 +64,21 @@ Files in the given directory will be served as static assets. Options to pass to the server in case `--serve` or `--esm` is being used. Currently only `--server-option.port` for passing the port to use is supported. + +## Spec + +The `spec` argument can be a list of files or a glob pattern that will be resolved by Mochify. + +``` +mochify ./src/foo.test.js ./src/bar.test.js +mochify ./src/*.test.js # Let the shell handle glob expansion +mochify "./src/*.test.js" # Let Mochify handle glob expansion +``` + +### Reading a bundle from `stdin` + +When given `-` as the spec, Mochify expects to read a bundled test suite from `stdin`: + +``` +browserify -t babelify ./src/*.test.js | mochify - +``` diff --git a/cli/index.js b/cli/index.js index e298947c..71389857 100755 --- a/cli/index.js +++ b/cli/index.js @@ -72,6 +72,10 @@ const opts = yargs(hideBin(process.argv)) '$0 --config mochify.webdriver.js "./src/**/*.test.js" ', 'Run all tests matching the given spec using the configuration from mochify.webdriver.js.' ) + .example( + 'browserify "./src/**/*.test.js" | $0 -', + 'Read a bundled test suite from stdin.' + ) .epilogue( `Mochify Resources: GitHub: https://github.com/mantoni/mochify.js` diff --git a/mochify/index.js b/mochify/index.js index cf4926da..1959fc79 100644 --- a/mochify/index.js +++ b/mochify/index.js @@ -24,7 +24,7 @@ async function mochify(options = {}) { const mocha_runner = createMochaRunner(config.reporter || 'spec'); const { mochifyDriver } = resolveMochifyDriver(config.driver); - const [mocha, client, files] = await Promise.all([ + const [mocha, client, resolved_spec] = await Promise.all([ readFile(require.resolve('mocha/mocha.js'), 'utf8'), readFile(require.resolve('./client'), 'utf8'), resolveSpec(config.spec) @@ -36,7 +36,7 @@ async function mochify(options = {}) { let server = null; if (config.serve || config.esm) { const _scripts = [mocha, configured_client]; - const _modules = config.esm ? files : []; + const _modules = config.esm ? resolved_spec : []; server = await startServer( config.serve || process.cwd(), Object.assign({ _scripts, _modules }, config.server_options) @@ -47,7 +47,7 @@ async function mochify(options = {}) { const driver_promise = mochifyDriver(driver_options); const bundler_promise = config.esm ? Promise.resolve('') - : resolveBundle(config.bundle, files); + : resolveBundle(config.bundle, resolved_spec); let driver, bundle; try { diff --git a/mochify/lib/resolve-bundle.js b/mochify/lib/resolve-bundle.js index d8767771..7f01ec4d 100644 --- a/mochify/lib/resolve-bundle.js +++ b/mochify/lib/resolve-bundle.js @@ -6,18 +6,21 @@ const { parseArgsStringToArgv } = require('string-argv'); exports.resolveBundle = resolveBundle; -async function resolveBundle(command, files) { - if (typeof files === 'object' && typeof files.pipe === 'function') { - return bufferStream(files); +async function resolveBundle(command, resolved_spec) { + if ( + typeof resolved_spec === 'object' && + typeof resolved_spec.pipe === 'function' + ) { + return bufferStream(resolved_spec); } if (!command) { - return concatFiles(files); + return concatFiles(resolved_spec); } const [cmd, ...args] = parseArgsStringToArgv(command); - const result = await execa(cmd, args.concat(files), { + const result = await execa(cmd, args.concat(resolved_spec), { preferLocal: true }); @@ -35,9 +38,9 @@ async function concatFiles(files) { function bufferStream(stream) { return new Promise((resolve, reject) => { - const bs = []; - stream.on('data', (chunk) => bs.push(chunk)); + const buffers = []; + stream.on('data', (chunk) => buffers.push(chunk)); stream.on('error', (err) => reject(err)); - stream.on('end', () => resolve(Buffer.concat(bs).toString())); + stream.on('end', () => resolve(Buffer.concat(buffers).toString('utf8'))); }); } From 603069f5e4985185b21a9875f65854eb151826db Mon Sep 17 00:00:00 2001 From: Frederik Ring Date: Fri, 17 Sep 2021 13:50:14 +0200 Subject: [PATCH 5/6] Consolidate option validation logic --- mochify/index.js | 6 ------ mochify/lib/validate-config.js | 5 +++++ mochify/lib/validate-config.test.js | 12 +++++++++--- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/mochify/index.js b/mochify/index.js index 1959fc79..55b38146 100644 --- a/mochify/index.js +++ b/mochify/index.js @@ -78,12 +78,6 @@ async function mochify(options = {}) { } function resolveMochifyDriver(name) { - if (!name) { - throw new Error( - 'Specifying a driver option is required. Mochify drivers need to be installed separately from the API or the CLI.' - ); - } - let driverModule; try { // eslint-disable-next-line node/global-require diff --git a/mochify/lib/validate-config.js b/mochify/lib/validate-config.js index 9a69e4c8..a6cdb1b1 100644 --- a/mochify/lib/validate-config.js +++ b/mochify/lib/validate-config.js @@ -3,6 +3,11 @@ exports.validateConfig = validateConfig; function validateConfig(config) { + if (!config.driver) { + return new Error( + 'Specifying a `driver` is required. Mochify drivers need to be installed separately from the API or the CLI.' + ); + } if (config.esm && config.bundle) { return new Error('`esm` cannot be used in conjunction with `bundle`'); } diff --git a/mochify/lib/validate-config.test.js b/mochify/lib/validate-config.test.js index 2957363a..69799f9c 100644 --- a/mochify/lib/validate-config.test.js +++ b/mochify/lib/validate-config.test.js @@ -7,21 +7,27 @@ const { validateConfig } = require('./validate-config'); describe('mochify/lib/validate-config', () => { it('returns an error when esm and bundle are given', () => { assert.isError( - validateConfig({ esm: true, bundle: 'browserify', spec: './test.js' }) + validateConfig({ + driver: 'puppeteer', + esm: true, + bundle: 'browserify', + spec: './test.js' + }) ); }); it('returns an error when bundle and a stream spec are given', () => { assert.isError( validateConfig({ + driver: 'puppeteer', bundle: 'browserify', spec: fs.createReadStream(__filename) }) ); }); - it('returns null on an empty config', () => { - assert.isNull(validateConfig({})); + it('returns an error on an empty config', () => { + assert.isError(validateConfig({})); }); it('returns null on a valid config', () => { From 878518f9cad555282d43e3e40118778d99daf4cc Mon Sep 17 00:00:00 2001 From: Frederik Ring Date: Fri, 17 Sep 2021 19:21:28 +0200 Subject: [PATCH 6/6] Throw directly when config validation fails --- mochify/index.js | 6 +----- mochify/lib/validate-config.js | 7 +++---- mochify/lib/validate-config.test.js | 22 +++++++++++----------- 3 files changed, 15 insertions(+), 20 deletions(-) diff --git a/mochify/index.js b/mochify/index.js index 55b38146..2b323f49 100644 --- a/mochify/index.js +++ b/mochify/index.js @@ -14,11 +14,7 @@ exports.mochify = mochify; async function mochify(options = {}) { const config = await loadConfig(options); - - const validation_error = validateConfig(config); - if (validation_error) { - throw validation_error; - } + validateConfig(config); // Create runner early to verify the reporter exists: const mocha_runner = createMochaRunner(config.reporter || 'spec'); diff --git a/mochify/lib/validate-config.js b/mochify/lib/validate-config.js index a6cdb1b1..0223bf7d 100644 --- a/mochify/lib/validate-config.js +++ b/mochify/lib/validate-config.js @@ -4,19 +4,18 @@ exports.validateConfig = validateConfig; function validateConfig(config) { if (!config.driver) { - return new Error( + throw new Error( 'Specifying a `driver` is required. Mochify drivers need to be installed separately from the API or the CLI.' ); } if (config.esm && config.bundle) { - return new Error('`esm` cannot be used in conjunction with `bundle`'); + throw new Error('`esm` cannot be used in conjunction with `bundle`'); } if ( config.bundle && typeof config.spec === 'object' && typeof config.spec.pipe === 'function' ) { - return new Error('`bundle` cannot be used when `spec` is a stream.'); + throw new Error('`bundle` cannot be used when `spec` is a stream.'); } - return null; } diff --git a/mochify/lib/validate-config.test.js b/mochify/lib/validate-config.test.js index 69799f9c..f6a6cfd6 100644 --- a/mochify/lib/validate-config.test.js +++ b/mochify/lib/validate-config.test.js @@ -1,42 +1,42 @@ 'use strict'; const fs = require('fs'); -const { assert } = require('@sinonjs/referee-sinon'); +const { assert, refute } = require('@sinonjs/referee-sinon'); const { validateConfig } = require('./validate-config'); describe('mochify/lib/validate-config', () => { it('returns an error when esm and bundle are given', () => { - assert.isError( + assert.exception(() => { validateConfig({ driver: 'puppeteer', esm: true, bundle: 'browserify', spec: './test.js' - }) - ); + }); + }); }); it('returns an error when bundle and a stream spec are given', () => { - assert.isError( + assert.exception(() => { validateConfig({ driver: 'puppeteer', bundle: 'browserify', spec: fs.createReadStream(__filename) - }) - ); + }); + }); }); it('returns an error on an empty config', () => { - assert.isError(validateConfig({})); + assert.exception(() => validateConfig({})); }); it('returns null on a valid config', () => { - assert.isNull( + refute.exception(() => { validateConfig({ bundle: 'browserify -t babelify', spec: './test.js', driver: 'puppeteer' - }) - ); + }); + }); }); });