👷 proper IPCRunner, IPCReporter for electron mocha test runner

This commit is contained in:
Joao Moreno 2017-04-28 12:57:09 +02:00
parent 6546fe5e87
commit 0ff4498392
2 changed files with 128 additions and 37 deletions

View file

@ -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 <pattern>').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));
}
});

View file

@ -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');
});
});
}