diff --git a/test/electron/index.js b/test/electron/index.js index 3e72fa6391c..0337a1421f7 100644 --- a/test/electron/index.js +++ b/test/electron/index.js @@ -6,6 +6,9 @@ const { app, BrowserWindow, ipcMain } = require('electron'); const { tmpdir } = require('os'); const { join } = require('path'); +const path = require('path'); +const mocha = require('mocha'); +const events = require('events'); const optimist = require('optimist') .describe('grep', 'only run tests matching ').alias('grep', 'g').alias('grep', 'f').string('grep') @@ -14,6 +17,7 @@ const optimist = require('optimist') .describe('build', 'run with build output (out-build)').boolean('build') .describe('coverage', 'generate coverage report').boolean('coverage') .describe('debug', 'open dev tools, keep window open, reuse app data').string('debug') + .describe('reporter', 'the mocha reporter').string('reporter').default('reporter', 'spec') .describe('help', 'show the help').alias('help', 'h'); const argv = optimist.argv; @@ -27,6 +31,59 @@ if (!argv.debug) { app.setPath('userData', join(tmpdir(), `vscode-tests-${Date.now()}`)); } +function deserializeSuite(suite) { + return { + title: suite.title, + fullTitle: () => suite.fullTitle, + timeout: () => suite.timeout, + retries: () => suite.retries, + enableTimeouts: () => suite.enableTimeouts, + slow: () => suite.slow, + bail: () => suite.bail, + }; +} + +function deserializeRunnable(runnable) { + return { + title: runnable.title, + fullTitle: () => runnable.fullTitle, + async: runnable.async, + slow: () => runnable.slow, + speed: runnable.speed, + duration: runnable.duration + }; +} + +function deserializeError(err) { + const inspect = err.inspect; + err.inspect = () => inspect; + return err; +} + +class IPCRunner extends events.EventEmitter { + + constructor() { + super(); + + this.didFail = false; + + ipcMain.on('start', () => this.emit('start')); + ipcMain.on('end', () => this.emit('end')); + ipcMain.on('suite', (e, suite) => this.emit('suite', deserializeSuite(suite))); + ipcMain.on('suite end', (e, suite) => this.emit('suite end', deserializeSuite(suite))); + ipcMain.on('test', (e, test) => this.emit('test', deserializeRunnable(test))); + ipcMain.on('test end', (e, test) => this.emit('test end', deserializeRunnable(test))); + ipcMain.on('hook', (e, hook) => this.emit('hook', deserializeRunnable(hook))); + ipcMain.on('hook end', (e, hook) => this.emit('hook end', deserializeRunnable(hook))); + ipcMain.on('pass', (e, test) => this.emit('pass', deserializeRunnable(test))); + ipcMain.on('fail', (e, test, err) => { + this.didFail = true; + this.emit('fail', deserializeRunnable(test), deserializeError(err)); + }); + ipcMain.on('pending', (e, test) => this.emit('pending', deserializeRunnable(test))); + } +} + app.on('ready', () => { const win = new BrowserWindow({ @@ -49,28 +106,20 @@ app.on('ready', () => { win.loadURL(`file://${__dirname}/renderer.html`); + const reporterPath = path.join(path.dirname(require.resolve('mocha')), 'lib', 'reporters', argv.reporter); + let Reporter; - const _failures = []; - ipcMain.on('fail', (e, test) => { - _failures.push(test); - process.stdout.write('X'); - }); - ipcMain.on('pass', () => { - process.stdout.write('.'); - }); + try { + Reporter = require(reporterPath); + } catch (err) { + console.warn(`could not load reporter: ${argv.reporter}`); + Reporter = mocha.reporters.Spec; + } - ipcMain.on('done', () => { + const runner = new IPCRunner(); + new Reporter(runner); - console.log(`\nDone with ${_failures.length} failures.\n`); - - for (const fail of _failures) { - console.error(fail.title); - console.error(fail.stack); - console.error('\n'); - } - - if (!argv.debug) { - app.exit(_failures.length > 0 ? 1 : 0); - } - }); + if (!argv.debug) { + ipcMain.on('all done', () => app.exit(runner.didFail ? 1 : 0)); + } }); diff --git a/test/electron/renderer.js b/test/electron/renderer.js index 0db3db2257a..21424034851 100644 --- a/test/electron/renderer.js +++ b/test/electron/renderer.js @@ -13,7 +13,6 @@ const minimatch = require('minimatch'); const istanbul = require('istanbul'); const i_remap = require('remap-istanbul/lib/remap'); - let _tests_glob = '**/test/**/*.test.js'; let loader; let _out; @@ -180,6 +179,58 @@ function loadTests(opts) { }); } +function serializeSuite(suite) { + return { + title: suite.title, + fullTitle: suite.fullTitle(), + timeout: suite.timeout(), + retries: suite.retries(), + enableTimeouts: suite.enableTimeouts(), + slow: suite.slow(), + bail: suite.bail() + }; +} + +function serializeRunnable(runnable) { + return { + title: runnable.title, + fullTitle: runnable.fullTitle(), + async: runnable.async, + slow: runnable.slow(), + speed: runnable.speed, + duration: runnable.duration + }; +} + +function serializeError(err) { + return { + message: err.message, + stack: err.stack, + actual: err.actual, + expected: err.expected, + uncaught: err.uncaught, + showDiff: err.showDiff, + inspect: typeof err.inspect === 'function' ? err.inspect() : '' + }; +} + +class IPCReporter { + + constructor(runner) { + runner.on('start', () => ipcRenderer.send('start')); + runner.on('end', () => ipcRenderer.send('end')); + runner.on('suite', suite => ipcRenderer.send('suite', serializeSuite(suite))); + runner.on('suite end', suite => ipcRenderer.send('suite end', serializeSuite(suite))); + runner.on('test', test => ipcRenderer.send('test', serializeRunnable(test))); + runner.on('test end', test => ipcRenderer.send('test end', serializeRunnable(test))); + runner.on('hook', hook => ipcRenderer.send('hook', serializeRunnable(hook))); + runner.on('hook end', hook => ipcRenderer.send('hook end', serializeRunnable(hook))); + runner.on('pass', test => ipcRenderer.send('pass', serializeRunnable(test))); + runner.on('fail', (test, err) => ipcRenderer.send('fail', serializeRunnable(test), serializeError(err))); + runner.on('pending', test => ipcRenderer.send('pending', serializeRunnable(test))); + } +} + function runTests(opts) { return loadTests(opts).then(() => { @@ -188,24 +239,15 @@ function runTests(opts) { mocha.grep(opts.grep); } - const runner = mocha.run(() => { + if (!opts.debug) { + mocha.reporter(IPCReporter); + } + + mocha.run(() => { createCoverageReport(opts).then(() => { - ipcRenderer.send('done'); + ipcRenderer.send('all done'); }); }); - - runner.on('fail', function (test) { - ipcRenderer.send('fail', { - title: test.fullTitle(), - stack: test.err.stack - }); - console.error(test.fullTitle()); - console.error(test.err.stack); - }); - - runner.on('pass', function () { - ipcRenderer.send('pass'); - }); }); }