From 30bc605c849fa3f09e4db29b496a98f89fd2f9ec Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Fri, 15 Jul 2022 16:04:19 +0200 Subject: [PATCH] [FIX] lifecyle_hooks: correctly wrap errors in async code Before this commit, wrapping an error occurring in async code would result in an unhandledpromise exception, because we created another promise, that would be rejected and that we didn't catch. --- src/runtime/lifecycle_hooks.ts | 2 +- .../__snapshots__/error_handling.test.ts.snap | 26 +++++++++++++++++++ tests/components/error_handling.test.ts | 24 +++++++++++++++++ 3 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/runtime/lifecycle_hooks.ts b/src/runtime/lifecycle_hooks.ts index 16984ef3..e8ed942d 100644 --- a/src/runtime/lifecycle_hooks.ts +++ b/src/runtime/lifecycle_hooks.ts @@ -15,7 +15,7 @@ function wrapError(fn: (...args: any[]) => any, hookName: string) { if (hookName === "onWillStart" || hookName === "onWillUpdateProps") { const fiber = node.fiber; Promise.race([ - result, + result.catch(() => {}), new Promise((resolve) => setTimeout(() => resolve(TIMEOUT), 3000)), ]).then((res) => { if (res === TIMEOUT && node.fiber === fiber) { diff --git a/tests/components/__snapshots__/error_handling.test.ts.snap b/tests/components/__snapshots__/error_handling.test.ts.snap index bd70be30..f1050afa 100644 --- a/tests/components/__snapshots__/error_handling.test.ts.snap +++ b/tests/components/__snapshots__/error_handling.test.ts.snap @@ -1257,6 +1257,19 @@ exports[`errors and promises an error in willPatch call will reject the render p }" `; +exports[`errors and promises error type is kept when it is wrapped 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`
abc
\`); + + return function template(ctx, node, key = \\"\\") { + return block1(); + } +}" +`; + exports[`errors and promises errors in mounted and in willUnmount 1`] = ` "function anonymous(app, bdom, helpers ) { @@ -1296,3 +1309,16 @@ exports[`errors and promises errors in rerender 1`] = ` } }" `; + +exports[`errors and promises wrapped errors in async code are correctly caught 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`
abc
\`); + + return function template(ctx, node, key = \\"\\") { + return block1(); + } +}" +`; diff --git a/tests/components/error_handling.test.ts b/tests/components/error_handling.test.ts index d1d2aec1..331f10ab 100644 --- a/tests/components/error_handling.test.ts +++ b/tests/components/error_handling.test.ts @@ -264,6 +264,30 @@ describe("errors and promises", () => { expect(error!.message).toBe("Tokenizer error: could not tokenize `{ 'invalid: 5 }`"); }); + test("wrapped errors in async code are correctly caught", async () => { + class Root extends Component { + static template = xml`
abc
`; + setup() { + onWillStart(async () => { + await Promise.resolve(); + throw new Error("boom in onWillStart"); + }); + } + } + + let error: any; + try { + await mount(Root, fixture, { test: true }); + } catch (e) { + error = e; + } + expect(error!).toBeDefined(); + expect(error!.message).toBe( + `The following error occurred in onWillStart: "boom in onWillStart"` + ); + await new Promise((r) => setTimeout(r, 0)); // wait for the rejection event to bubble + }); + test("an error in willPatch call will reject the render promise", async () => { class Root extends Component { static template = xml`
`;