Skip to content

Commit 78afb92

Browse files
regevbrSBoudrias
authored andcommitted
Fix generator error handling (Fix #89) (#101)
* Fix generator error handling (Fix #89) * Fix generator error handling (Fix #89) * Fix generator error handling (Fix #89)
1 parent 8bfa1ed commit 78afb92

File tree

2 files changed

+67
-1
lines changed

2 files changed

+67
-1
lines changed

lib/environment.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,25 @@ function splitArgsFromString(argsString) {
3131
return result;
3232
}
3333

34+
/**
35+
* Wrap callback so it can't get called twice
36+
*/
37+
const callbackWrapper = (generator, done) => {
38+
if (!done) {
39+
return _.noop();
40+
}
41+
let callbackHandled = false;
42+
const callback = err => {
43+
if (!callbackHandled) {
44+
callbackHandled = true;
45+
done(err);
46+
}
47+
};
48+
// If error was thrown, make sure it is handled and only once
49+
generator.on('error', callback);
50+
return callback;
51+
};
52+
3453
/**
3554
* `Environment` object is responsible of handling the lifecyle and bootstrap
3655
* of generators in a specific environment (your app).
@@ -453,7 +472,9 @@ class Environment extends EventEmitter {
453472
return console.log(generator.help());
454473
}
455474

456-
return generator.run(done);
475+
const _callbackWrapper = callbackWrapper(generator, done);
476+
477+
return generator.run(_callbackWrapper);
457478
}
458479

459480
/**

test/environment.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,24 @@ describe('Environment', () => {
177177
exec() {}
178178
};
179179

180+
this.PromiseFailingStub = class extends Generator {
181+
182+
install() {
183+
return Promise.reject(new Error('some error'));
184+
}
185+
};
186+
187+
this.EventFailingStub = class extends Generator {
188+
189+
install() {
190+
return this.emit('error', new Error('some error'));
191+
}
192+
};
193+
180194
this.runMethod = sinon.spy(Generator.prototype, 'run');
181195
this.env.registerStub(this.Stub, 'stub:run');
196+
this.env.registerStub(this.PromiseFailingStub, 'promisefailingstub:run');
197+
this.env.registerStub(this.EventFailingStub, 'eventfailingstub:run');
182198
});
183199

184200
afterEach(function () {
@@ -248,6 +264,35 @@ describe('Environment', () => {
248264
this.env.run('some:unknown:generator');
249265
});
250266

267+
it('generator promise error calls callback properly', function (done) {
268+
this.env.run('promisefailingstub:run', err => {
269+
assert.ok(this.runMethod.calledOnce);
270+
assert.ok(err instanceof Error);
271+
assert.equal(err.message, 'some error');
272+
done();
273+
});
274+
});
275+
276+
it('generator error event calls callback properly', function (done) {
277+
this.env.run('eventfailingstub:run', err => {
278+
assert.ok(this.runMethod.calledOnce);
279+
assert.ok(err instanceof Error);
280+
assert.equal(err.message, 'some error');
281+
done();
282+
});
283+
});
284+
285+
it('generator error event emits error event when no callback passed', function (done) {
286+
const generator = this.env.run('eventfailingstub:run');
287+
assert.equal(generator.listenerCount('error'), 0);
288+
generator.on('error', err => {
289+
assert.ok(this.runMethod.calledOnce);
290+
assert.ok(err instanceof Error);
291+
assert.equal(err.message, 'some error');
292+
done();
293+
});
294+
});
295+
251296
it('returns the generator', function () {
252297
assert.ok(this.env.run('stub:run') instanceof Generator);
253298
});

0 commit comments

Comments
 (0)