From 74dfa4eef902a3e8acd39dcd4bc40ea22b588075 Mon Sep 17 00:00:00 2001 From: Wout Mertens Date: Tue, 20 Feb 2018 16:15:08 +0100 Subject: [PATCH] HookCodeFactory: fail verbosely on non-Promise Promise errors in the hooks are hard to debug, this way it's easier to find the problem --- lib/HookCodeFactory.js | 7 +- lib/__tests__/HookTester.js | 5 ++ .../__snapshots__/AsyncSeriesHooks.js.snap | 17 +++++ .../__snapshots__/HookCodeFactory.js.snap | 75 +++++++++++++++---- 4 files changed, 87 insertions(+), 17 deletions(-) diff --git a/lib/HookCodeFactory.js b/lib/HookCodeFactory.js index 7d8ccdb..bf17e1d 100644 --- a/lib/HookCodeFactory.js +++ b/lib/HookCodeFactory.js @@ -164,9 +164,12 @@ class HookCodeFactory { break; case "promise": code += `var _hasResult${tapIndex} = false;\n`; - code += `_fn${tapIndex}(${this.args({ + code += `var p = _fn${tapIndex}(${this.args({ before: tap.context ? "_context" : undefined - })}).then(_result${tapIndex} => {\n`; + })});\n`; + code += `if (!(p && p.then))\n`; + code += ` throw new Error('Callback did not return promise. Code:\\n\\n' + _fn${tapIndex} +'\\n\\nResult: '+p+'\\n');\n`; + code += `p.then(_result${tapIndex} => {\n`; code += `_hasResult${tapIndex} = true;\n`; if(onResult) { code += onResult(`_result${tapIndex}`); diff --git a/lib/__tests__/HookTester.js b/lib/__tests__/HookTester.js index a0a5c75..09408de 100644 --- a/lib/__tests__/HookTester.js +++ b/lib/__tests__/HookTester.js @@ -69,6 +69,11 @@ class HookTester { } async runForLoopAsync(result, method) { + { + const hook = this.createHook([], `${method}BrokenPromise`); + hook.tapPromise("promise", () => "this is not a promise"); + result[`${method}BrokenPromise`] = await this.gainResult(cb => hook[method](cb)); + } { const hook = this.createHook([], `${method}SinglePromise`); hook.tapPromise("promise", () => { diff --git a/lib/__tests__/__snapshots__/AsyncSeriesHooks.js.snap b/lib/__tests__/__snapshots__/AsyncSeriesHooks.js.snap index 712905a..7106653 100644 --- a/lib/__tests__/__snapshots__/AsyncSeriesHooks.js.snap +++ b/lib/__tests__/__snapshots__/AsyncSeriesHooks.js.snap @@ -1431,6 +1431,14 @@ Object { exports[`AsyncSeriesLoopHook should have to correct behavior 1`] = ` Object { "async": Object { + "callAsyncBrokenPromise": Object { + "error": "Callback did not return promise. Code: + +() => \\"this is not a promise\\" + +Result: this is not a promise +", + }, "callAsyncMixed": Object { "type": "async", "value": undefined, @@ -1460,6 +1468,15 @@ Object { "value": undefined, }, "callAsyncSinglePromiseCalled": 42, + "promiseBrokenPromise": Object { + "error": "Callback did not return promise. Code: + +() => \\"this is not a promise\\" + +Result: this is not a promise +", + "type": "promise", + }, "promiseMixed": Object { "type": "promise", "value": undefined, diff --git a/lib/__tests__/__snapshots__/HookCodeFactory.js.snap b/lib/__tests__/__snapshots__/HookCodeFactory.js.snap index f0fb444..2f023b3 100644 --- a/lib/__tests__/__snapshots__/HookCodeFactory.js.snap +++ b/lib/__tests__/__snapshots__/HookCodeFactory.js.snap @@ -27,7 +27,10 @@ onDone(); exports[`HookCodeFactory callTap (no args, no intercept) promise with onResult 1`] = ` "var _fn2 = _x[2]; var _hasResult2 = false; -_fn2().then(_result2 => { +var p = _fn2(); +if (!(p && p.then)) + throw new Error('Callback did not return promise. Code:\\\\n\\\\n' + _fn2 +'\\\\n\\\\nResult: '+p+'\\\\n'); +p.then(_result2 => { _hasResult2 = true; onResult(_result2); }, _err2 => { @@ -40,7 +43,10 @@ onError(_err2); exports[`HookCodeFactory callTap (no args, no intercept) promise without onResult 1`] = ` "var _fn2 = _x[2]; var _hasResult2 = false; -_fn2().then(_result2 => { +var p = _fn2(); +if (!(p && p.then)) + throw new Error('Callback did not return promise. Code:\\\\n\\\\n' + _fn2 +'\\\\n\\\\nResult: '+p+'\\\\n'); +p.then(_result2 => { _hasResult2 = true; onDone(); }, _err2 => { @@ -107,7 +113,10 @@ onDone(); exports[`HookCodeFactory callTap (with args, no intercept) promise with onResult 1`] = ` "var _fn2 = _x[2]; var _hasResult2 = false; -_fn2(a, b, c).then(_result2 => { +var p = _fn2(a, b, c); +if (!(p && p.then)) + throw new Error('Callback did not return promise. Code:\\\\n\\\\n' + _fn2 +'\\\\n\\\\nResult: '+p+'\\\\n'); +p.then(_result2 => { _hasResult2 = true; onResult(_result2); }, _err2 => { @@ -120,7 +129,10 @@ onError(_err2); exports[`HookCodeFactory callTap (with args, no intercept) promise without onResult 1`] = ` "var _fn2 = _x[2]; var _hasResult2 = false; -_fn2(a, b, c).then(_result2 => { +var p = _fn2(a, b, c); +if (!(p && p.then)) + throw new Error('Callback did not return promise. Code:\\\\n\\\\n' + _fn2 +'\\\\n\\\\nResult: '+p+'\\\\n'); +p.then(_result2 => { _hasResult2 = true; onDone(); }, _err2 => { @@ -196,7 +208,10 @@ _interceptors[0].tap(_tap2); _interceptors[1].tap(_tap2); var _fn2 = _x[2]; var _hasResult2 = false; -_fn2(a, b, c).then(_result2 => { +var p = _fn2(a, b, c); +if (!(p && p.then)) + throw new Error('Callback did not return promise. Code:\\\\n\\\\n' + _fn2 +'\\\\n\\\\nResult: '+p+'\\\\n'); +p.then(_result2 => { _hasResult2 = true; onResult(_result2); }, _err2 => { @@ -212,7 +227,10 @@ _interceptors[0].tap(_tap2); _interceptors[1].tap(_tap2); var _fn2 = _x[2]; var _hasResult2 = false; -_fn2(a, b, c).then(_result2 => { +var p = _fn2(a, b, c); +if (!(p && p.then)) + throw new Error('Callback did not return promise. Code:\\\\n\\\\n' + _fn2 +'\\\\n\\\\nResult: '+p+'\\\\n'); +p.then(_result2 => { _hasResult2 = true; onDone(); }, _err2 => { @@ -288,7 +306,10 @@ if(_loopAsync) _looper(); } else { var _fn2 = _x[2]; var _hasResult2 = false; -_fn2(a, b, c).then(_result2 => { +var p = _fn2(a, b, c); +if (!(p && p.then)) + throw new Error('Callback did not return promise. Code:\\\\n\\\\n' + _fn2 +'\\\\n\\\\nResult: '+p+'\\\\n'); +p.then(_result2 => { _hasResult2 = true; if(_result2 !== undefined) { _loop = true; @@ -352,7 +373,10 @@ _done(); if(_counter <= 0) break; var _fn2 = _x[2]; var _hasResult2 = false; -_fn2(a, b, c).then(_result2 => { +var p = _fn2(a, b, c); +if (!(p && p.then)) + throw new Error('Callback did not return promise. Code:\\\\n\\\\n' + _fn2 +'\\\\n\\\\nResult: '+p+'\\\\n'); +p.then(_result2 => { _hasResult2 = true; if(_counter > 0) { onResult(2, _result2, () => { @@ -384,7 +408,10 @@ onError(1, _err1); onResult(1, _result1, () => { var _fn2 = _x[2]; var _hasResult2 = false; -_fn2(a, b, c).then(_result2 => { +var p = _fn2(a, b, c); +if (!(p && p.then)) + throw new Error('Callback did not return promise. Code:\\\\n\\\\n' + _fn2 +'\\\\n\\\\nResult: '+p+'\\\\n'); +p.then(_result2 => { _hasResult2 = true; onResult(2, _result2, () => { onDone(); @@ -423,7 +450,10 @@ if(_loopAsync) _looper(); } else { var _fn1 = _x[1]; var _hasResult1 = false; -_fn1(a, b, c).then(_result1 => { +var p = _fn1(a, b, c); +if (!(p && p.then)) + throw new Error('Callback did not return promise. Code:\\\\n\\\\n' + _fn1 +'\\\\n\\\\nResult: '+p+'\\\\n'); +p.then(_result1 => { _hasResult1 = true; if(_result1 !== undefined) { _loop = true; @@ -489,7 +519,10 @@ _done(); if(_counter <= 0) break; var _fn1 = _x[1]; var _hasResult1 = false; -_fn1(a, b, c).then(_result1 => { +var p = _fn1(a, b, c); +if (!(p && p.then)) + throw new Error('Callback did not return promise. Code:\\\\n\\\\n' + _fn1 +'\\\\n\\\\nResult: '+p+'\\\\n'); +p.then(_result1 => { _hasResult1 = true; if(_counter > 0) { onResult(1, _result1, () => { @@ -529,7 +562,10 @@ onError(0, _err0); onResult(0, _result0, () => { var _fn1 = _x[1]; var _hasResult1 = false; -_fn1(a, b, c).then(_result1 => { +var p = _fn1(a, b, c); +if (!(p && p.then)) + throw new Error('Callback did not return promise. Code:\\\\n\\\\n' + _fn1 +'\\\\n\\\\nResult: '+p+'\\\\n'); +p.then(_result1 => { _hasResult1 = true; onResult(1, _result1, () => { var _fn2 = _x[2]; @@ -740,7 +776,10 @@ do { _loop = false; var _fn0 = _x[0]; var _hasResult0 = false; -_fn0(a, b, c).then(_result0 => { +var p = _fn0(a, b, c); +if (!(p && p.then)) + throw new Error('Callback did not return promise. Code:\\\\n\\\\n' + _fn0 +'\\\\n\\\\nResult: '+p+'\\\\n'); +p.then(_result0 => { _hasResult0 = true; if(_result0 !== undefined) { _loop = true; @@ -764,7 +803,10 @@ _looper(); exports[`HookCodeFactory taps (single promise) callTapsParallel 1`] = ` "var _fn0 = _x[0]; var _hasResult0 = false; -_fn0(a, b, c).then(_result0 => { +var p = _fn0(a, b, c); +if (!(p && p.then)) + throw new Error('Callback did not return promise. Code:\\\\n\\\\n' + _fn0 +'\\\\n\\\\nResult: '+p+'\\\\n'); +p.then(_result0 => { _hasResult0 = true; onResult(0, _result0, () => { onDone(); @@ -781,7 +823,10 @@ onError(0, _err0); exports[`HookCodeFactory taps (single promise) callTapsSeries 1`] = ` "var _fn0 = _x[0]; var _hasResult0 = false; -_fn0(a, b, c).then(_result0 => { +var p = _fn0(a, b, c); +if (!(p && p.then)) + throw new Error('Callback did not return promise. Code:\\\\n\\\\n' + _fn0 +'\\\\n\\\\nResult: '+p+'\\\\n'); +p.then(_result0 => { _hasResult0 = true; onResult(0, _result0, () => { onDone();