diff --git a/src/runtime/lifecycle_hooks.ts b/src/runtime/lifecycle_hooks.ts index 0c98cbe86..992900864 100644 --- a/src/runtime/lifecycle_hooks.ts +++ b/src/runtime/lifecycle_hooks.ts @@ -28,7 +28,7 @@ function wrapError(fn: (...args: any[]) => any, hookName: string) { result.catch(() => {}), new Promise((resolve) => setTimeout(() => resolve(TIMEOUT), 3000)), ]).then((res) => { - if (res === TIMEOUT && node.fiber === fiber) { + if (res === TIMEOUT && node.fiber === fiber && node.status <= 2) { console.warn(timeoutError); } }); diff --git a/tests/components/__snapshots__/lifecycle.test.ts.snap b/tests/components/__snapshots__/lifecycle.test.ts.snap index 6d8cc8111..8b5bce58b 100644 --- a/tests/components/__snapshots__/lifecycle.test.ts.snap +++ b/tests/components/__snapshots__/lifecycle.test.ts.snap @@ -683,6 +683,19 @@ exports[`lifecycle hooks sub widget (inside sub node): hooks are correctly calle }" `; +exports[`lifecycle hooks timeout in onWillStart doesn't emit a warning if app is destroyed 1`] = ` +"function anonymous(app, bdom, helpers +) { + let { text, createBlock, list, multi, html, toggler, comment } = bdom; + + let block1 = createBlock(\`\`); + + return function template(ctx, node, key = \\"\\") { + return block1(); + } +}" +`; + exports[`lifecycle hooks timeout in onWillStart emits a warning 1`] = ` "function anonymous(app, bdom, helpers ) { diff --git a/tests/components/lifecycle.test.ts b/tests/components/lifecycle.test.ts index a140b2b4a..d223f54e7 100644 --- a/tests/components/lifecycle.test.ts +++ b/tests/components/lifecycle.test.ts @@ -1,5 +1,9 @@ -import { App, Component, mount, onMounted, onWillStart, useState, xml } from "../../src"; import { + App, + Component, + mount, + useState, + xml, onWillPatch, onWillUnmount, onPatched, @@ -7,7 +11,9 @@ import { onWillRender, onWillDestroy, onRendered, -} from "../../src/runtime/lifecycle_hooks"; + onMounted, + onWillStart, +} from "../../src"; import { status } from "../../src/runtime/status"; import { elem, @@ -117,24 +123,60 @@ describe("lifecycle hooks", () => { timeoutCbs[++timeoutId] = cb; return timeoutId; }) as any; - class Test extends Component { - static template = xml``; - setup() { - onWillStart(() => new Promise(() => {})); + try { + class Test extends Component { + static template = xml``; + setup() { + onWillStart(() => new Promise(() => {})); + } + } + mount(Test, fixture, { test: true }); + nextTick(); + for (const id in timeoutCbs) { + timeoutCbs[id](); + delete timeoutCbs[id]; } + await nextMicroTick(); + await nextMicroTick(); + expect(console.warn).toHaveBeenCalledTimes(1); + expect(warnArgs![0]!.message).toBe("onWillStart's promise hasn't resolved after 3 seconds"); + } finally { + console.warn = warn; + window.setTimeout = setTimeout; + } + }); + + test("timeout in onWillStart doesn't emit a warning if app is destroyed", async () => { + const { warn } = console; + console.warn = jest.fn(); + const { setTimeout } = window; + let timeoutCbs: any = {}; + let timeoutId = 0; + window.setTimeout = ((cb: any) => { + timeoutCbs[++timeoutId] = cb; + return timeoutId; + }) as any; + try { + class Test extends Component { + static template = xml``; + setup() { + onWillStart(() => new Promise(() => {})); + } + } + const app = new App(Test, { test: true }); + app.mount(fixture); + app.destroy(); + for (const id in timeoutCbs) { + timeoutCbs[id](); + delete timeoutCbs[id]; + } + await nextMicroTick(); + await nextMicroTick(); + expect(console.warn).toHaveBeenCalledTimes(0); + } finally { + console.warn = warn; + window.setTimeout = setTimeout; } - mount(Test, fixture, { test: true }); - nextTick(); - for (const id in timeoutCbs) { - timeoutCbs[id](); - delete timeoutCbs[id]; - } - await nextMicroTick(); - await nextMicroTick(); - expect(console.warn).toHaveBeenCalledTimes(1); - expect(warnArgs![0]!.message).toBe("onWillStart's promise hasn't resolved after 3 seconds"); - console.warn = warn; - window.setTimeout = setTimeout; }); test("timeout in onWillUpdateProps emits a warning", async () => { @@ -162,25 +204,28 @@ describe("lifecycle hooks", () => { return timeoutId; }) as any; - parent.state.prop = 2; - let tick = nextTick(); - for (const id in timeoutCbs) { - timeoutCbs[id](); - delete timeoutCbs[id]; - } - await tick; - tick = nextTick(); - for (const id in timeoutCbs) { - timeoutCbs[id](); - delete timeoutCbs[id]; - } - await tick; - expect(console.warn).toHaveBeenCalledTimes(1); - expect(warnArgs![0]!.message).toBe( - "onWillUpdateProps's promise hasn't resolved after 3 seconds" - ); - console.warn = warn; - window.setTimeout = setTimeout; + try { + parent.state.prop = 2; + let tick = nextTick(); + for (const id in timeoutCbs) { + timeoutCbs[id](); + delete timeoutCbs[id]; + } + await tick; + tick = nextTick(); + for (const id in timeoutCbs) { + timeoutCbs[id](); + delete timeoutCbs[id]; + } + await tick; + expect(console.warn).toHaveBeenCalledTimes(1); + expect(warnArgs![0]!.message).toBe( + "onWillUpdateProps's promise hasn't resolved after 3 seconds" + ); + } finally { + console.warn = warn; + window.setTimeout = setTimeout; + } }); test("mounted hook is called if mounted in DOM", async () => {