Skip to content

Commit 95336e4

Browse files
authored
Merge pull request #125 from gemini-testing/fix/event.throws
fix: fail instead of hang on throws from event handlers
2 parents 2440b02 + 6c1706a commit 95336e4

File tree

2 files changed

+69
-4
lines changed

2 files changed

+69
-4
lines changed

lib/runner/mocha-runner/mocha-adapter.js

+29-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const path = require('path');
1111
const clearRequire = require('clear-require');
1212
const q = require('q');
1313
const _ = require('lodash');
14+
const EventEmitter = require('events').EventEmitter;
1415

1516
// Avoid mochajs warning about possible EventEmitter memory leak
1617
// https://nodejs.org/docs/latest/api/events.html#events_emitter_setmaxlisteners_n
@@ -46,6 +47,8 @@ module.exports = class MochaAdapter {
4647
this._injectSkip();
4748

4849
_.extend(global.hermione, {ctx});
50+
51+
this._errMonitor = new EventEmitter();
4952
}
5053

5154
applySkip(testSkipper) {
@@ -92,23 +95,48 @@ module.exports = class MochaAdapter {
9295
}
9396

9497
attachEmitFn(emit) {
98+
const _this = this;
99+
function monitoredEmit() {
100+
try {
101+
emit.apply(null, arguments);
102+
} catch (e) {
103+
_this._errMonitor.emit('err', e);
104+
throw e;
105+
}
106+
}
107+
108+
this._attachProxyReporter(monitoredEmit);
109+
this._passthroughFileEvents(monitoredEmit);
110+
111+
return this;
112+
}
113+
114+
_attachProxyReporter(emit) {
95115
const Reporter = _.partial(ProxyReporter, emit, () => this._getBrowser());
96116
this._mocha.reporter(Reporter);
117+
}
97118

119+
_passthroughFileEvents(emit) {
98120
const emit_ = (event, file) => emit(event, {
99121
file,
100122
hermione: global.hermione,
101123
browser: this._browserAgent.browserId,
102124
suite: this.suite
103125
});
126+
104127
this.suite.on('pre-require', (ctx, file) => emit_(RunnerEvents.BEFORE_FILE_READ, file));
105128
this.suite.on('post-require', (ctx, file) => emit_(RunnerEvents.AFTER_FILE_READ, file));
106129

107130
return this;
108131
}
109132

110133
run() {
111-
return q.Promise(this._mocha.run.bind(this._mocha));
134+
const defer = q.defer();
135+
136+
this._errMonitor.on('err', (err) => defer.reject(err));
137+
this._mocha.run((err) => err ? defer.reject(err) : defer.resolve());
138+
139+
return defer.promise;
112140
}
113141

114142
_injectSkip() {

test/lib/runner/mocha-runner/mocha-adapter.js

+40-3
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ describe('mocha-runner/mocha-adapter', () => {
7272
browserAgent = sinon.createStubInstance(BrowserAgent);
7373

7474
sandbox.stub(MochaStub.prototype);
75-
MochaStub.prototype.run.yields();
75+
MochaStub.prototype.run = (cb) => process.nextTick(cb);
7676
MochaStub.prototype.suite = mkSuiteStub_();
7777

7878
MochaAdapter = proxyquire('../../../../lib/runner/mocha-runner/mocha-adapter', {
@@ -524,8 +524,11 @@ describe('mocha-runner/mocha-adapter', () => {
524524

525525
attachEmitFn_(emitFn);
526526

527-
assert.calledOnce(ProxyReporter.prototype.__constructor);
528-
assert.calledWith(ProxyReporter.prototype.__constructor, emitFn);
527+
const emit_ = ProxyReporter.prototype.__constructor.firstCall.args[0];
528+
emit_('some-event', {some: 'data'});
529+
530+
assert.calledOnce(emitFn);
531+
assert.calledWith(emitFn, 'some-event', sinon.match({some: 'data'}));
529532
});
530533

531534
it('should pass to proxy reporter getter for requested browser', () => {
@@ -552,6 +555,40 @@ describe('mocha-runner/mocha-adapter', () => {
552555
assert.deepEqual(getBrowser(), {id: 'some-browser'});
553556
});
554557

558+
describe('if event handler throws', () => {
559+
const initBadHandler_ = (event, handler) => {
560+
const emitter = new EventEmitter();
561+
emitter.on(event, handler);
562+
563+
attachEmitFn_(emitter.emit.bind(emitter));
564+
return ProxyReporter.prototype.__constructor.firstCall.args[0];
565+
};
566+
567+
it('proxy should rethrow error', () => {
568+
const emit_ = initBadHandler_('foo', () => {
569+
throw new Error(new Error('bar'));
570+
});
571+
572+
assert.throws(() => emit_('foo'), /bar/);
573+
});
574+
575+
it('run should be rejected', () => {
576+
const emit_ = initBadHandler_('foo', () => {
577+
throw new Error('bar');
578+
});
579+
580+
const promise = mochaAdapter.run();
581+
582+
try {
583+
emit_('foo');
584+
} catch (e) {
585+
// eslint иди лесом
586+
}
587+
588+
return assert.isRejected(promise, /bar/);
589+
});
590+
});
591+
555592
describe('file events', () => {
556593
beforeEach(() => MochaAdapter.init());
557594
afterEach(() => delete global.hermione);

0 commit comments

Comments
 (0)