// import { App, Component, mount, onWillDestroy } from "../../src"; // import { // onError, // onMounted, // onPatched, // onWillPatch, // onWillStart, // onWillRender, // onRendered, // onWillUnmount, // useState, // xml, // } from "../../src/index"; // import { // logStep, // makeTestFixture, // nextTick, // nextMicroTick, // snapshotEverything, // useLogLifecycle, // nextAppError, // steps, // } from "../helpers"; // import { OwlError } from "../../src/common/owl_error"; // let fixture: HTMLElement; // snapshotEverything(); // let originalconsoleError = console.error; // let mockConsoleError: any; // let originalconsoleWarn = console.warn; // let mockConsoleWarn: any; // beforeEach(() => { // fixture = makeTestFixture(); // mockConsoleError = jest.fn(() => {}); // mockConsoleWarn = jest.fn(() => {}); // console.error = mockConsoleError; // console.warn = mockConsoleWarn; // }); // afterEach(() => { // console.error = originalconsoleError; // console.warn = originalconsoleWarn; // }); // describe("basics", () => { // test("no component catching error lead to full app destruction", async () => { // class ErrorComponent extends Component { // static template = xml`
hey
`; // } // class Parent extends Component { // static template = xml`
`; // static components = { ErrorComponent }; // state = { flag: false }; // } // const parent = await mount(Parent, fixture); // expect(fixture.innerHTML).toBe("
heyfalse
"); // parent.state.flag = true; // parent.render(); // await expect(nextAppError(parent.__owl__.app)).resolves.toThrow( // "An error occured in the owl lifecycle" // ); // expect(fixture.innerHTML).toBe(""); // expect(mockConsoleWarn).toBeCalledTimes(1); // }); // test("display a nice error if it cannot find component", async () => { // class SomeComponent extends Component {} // class Parent extends Component { // static template = xml``; // static components = { SomeComponent }; // } // const app = new App(Parent); // let error: Error; // const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); // await expect(nextAppError(app)).resolves.toThrow( // 'Cannot find the definition of component "SomeMispelledComponent"' // ); // await mountProm; // expect(error!).toBeDefined(); // expect(error!.message).toBe('Cannot find the definition of component "SomeMispelledComponent"'); // expect(console.error).toBeCalledTimes(0); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(1); // }); // test("display a nice error if it cannot find component (in dev mode)", async () => { // class SomeComponent extends Component {} // class Parent extends Component { // static template = xml``; // static components = { SomeComponent }; // } // const app = new App(Parent, { test: true }); // let error: Error; // const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); // await expect(nextAppError(app)).resolves.toThrow( // 'Cannot find the definition of component "SomeMispelledComponent"' // ); // await mountProm; // expect(error!).toBeDefined(); // expect(error!.message).toBe('Cannot find the definition of component "SomeMispelledComponent"'); // expect(console.error).toBeCalledTimes(0); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(1); // }); // test("display a nice error if a component is not a component", async () => { // function notAComponentConstructor() {} // class Parent extends Component { // static template = xml``; // static components = { SomeComponent: notAComponentConstructor }; // } // const app = new App(Parent as typeof Component); // let error: Error; // const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); // await expect(nextAppError(app)).resolves.toThrow( // '"SomeComponent" is not a Component. It must inherit from the Component class' // ); // await mountProm; // expect(error!).toBeDefined(); // expect(error!.message).toBe( // '"SomeComponent" is not a Component. It must inherit from the Component class' // ); // }); // test("display a nice error if the components key is missing with subcomponents", async () => { // class Parent extends Component { // static template = xml`
`; // } // const app = new App(Parent as typeof Component); // let error: Error; // const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); // await expect(nextAppError(app)).resolves.toThrow( // 'Cannot find the definition of component "MissingChild", missing static components key in parent' // ); // await mountProm; // expect(error!).toBeDefined(); // expect(error!.message).toBe( // 'Cannot find the definition of component "MissingChild", missing static components key in parent' // ); // }); // test("display a nice error if the root component template fails to compile", async () => { // // This is a special case: mount throws synchronously and we don't have any // // node which can handle the error, hence the different structure of this test // class Comp extends Component { // static template = xml`
test
`; // } // const app = new App(Comp); // let error: Error; // try { // await app.mount(fixture); // } catch (e) { // error = e as Error; // } // const expectedErrorMessage = `Failed to compile anonymous template: Unexpected identifier 'ctx' // generated code: // function(app, bdom, helpers) { // let { text, createBlock, list, multi, html, toggler, comment } = bdom; // let block1 = createBlock(\`
test
\`); // return function template(ctx, node, key = "") { // let attr1 = ctx['a']ctx['b']; // return block1([attr1]); // } // }`; // expect(error!).toBeDefined(); // expect(error!.message).toBe(expectedErrorMessage); // }); // test("display a nice error if a non-root component template fails to compile", async () => { // class Child extends Component { // static template = xml`
test
`; // } // class Parent extends Component { // static components = { Child }; // static template = xml``; // } // const expectedErrorMessage = `Failed to compile anonymous template: Unexpected identifier 'ctx' // generated code: // function(app, bdom, helpers) { // let { text, createBlock, list, multi, html, toggler, comment } = bdom; // let block1 = createBlock(\`
test
\`); // return function template(ctx, node, key = "") { // let attr1 = ctx['a']ctx['b']; // return block1([attr1]); // } // }`; // const app = new App(Parent as typeof Component); // let error: Error; // const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); // await expect(nextAppError(app)).resolves.toThrow(expectedErrorMessage); // await mountProm; // expect(error!).toBeDefined(); // expect(error!.message).toBe(expectedErrorMessage); // }); // test("simple catchError", async () => { // class Boom extends Component { // static template = xml`
`; // } // class Parent extends Component { // static template = xml` //
// Error // // // //
`; // static components = { Boom }; // error: any = false; // setup() { // onError((err) => { // this.error = err; // this.render(); // }); // } // } // await mount(Parent, fixture); // expect(fixture.innerHTML).toBe("
Error
"); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(0); // }); // }); // describe("errors and promises", () => { // test("a rendering error will reject the mount promise", async () => { // // we do not catch error in willPatch anymore // class Root extends Component { // static template = xml`
`; // } // const app = new App(Root); // let error: OwlError; // const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); // await expect(nextAppError(app)).resolves.toThrow("error occured in the owl lifecycle"); // await mountProm; // expect(error!).toBeDefined(); // expect(error!.cause).toBeDefined(); // const regexp = // /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g; // expect(error!.cause.message).toMatch(regexp); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleError).toBeCalledTimes(0); // }); // test("an error in mounted call will reject the mount promise", async () => { // class Root extends Component { // static template = xml`
abc
`; // setup() { // onMounted(() => { // throw new Error("boom"); // }); // } // } // const app = new App(Root); // let error: OwlError; // const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); // await expect(nextAppError(app)).resolves.toThrow("error occured in the owl lifecycle"); // await mountProm; // expect(error!).toBeDefined(); // expect(error!.cause).toBeDefined(); // expect(error!.cause.message).toBe("boom"); // expect(fixture.innerHTML).toBe(""); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(1); // }); // test("an error in onMounted callback will have the component's setup in its stack trace", async () => { // class Root extends Component { // static template = xml`
abc
`; // setup() { // onMounted(() => { // throw new Error("boom"); // }); // } // } // const app = new App(Root, { test: true }); // let error: OwlError; // const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); // await expect(nextAppError(app)).resolves.toThrow("error occurred in onMounted"); // await mountProm; // expect(error!).toBeDefined(); // expect(error!.stack).toContain("Root.setup"); // expect(error!.stack).toContain("error_handling.test.ts"); // expect(fixture.innerHTML).toBe(""); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(1); // }); // test("errors in onWillRender/onRender aren't wrapped more than once", async () => { // class Root extends Component { // static template = xml`
abc
`; // setup() { // onWillRender(() => { // throw new Error("boom in onWillRender"); // }); // onRendered(() => { // throw new Error("boom in onRendered"); // }); // } // } // const app = new App(Root, { test: true }); // let error: OwlError; // const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); // await expect(nextAppError(app)).resolves.toThrow("error occurred in onWillRender"); // await mountProm; // expect(error!).toBeDefined(); // expect(error!.message).toBe( // `The following error occurred in onWillRender: "boom in onWillRender"` // ); // }); // test("error while rendering component isn't wrapped by onWillRender/onRendered", async () => { // class App extends Component { // static template = xml`
abc
`; // setup() { // onWillRender(() => {}); // onRendered(() => {}); // } // } // let error: Error; // try { // await mount(App, fixture, { test: true }); // } catch (e) { // error = e as Error; // } // expect(error!).toBeDefined(); // 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"); // }); // } // } // const app = new App(Root, { test: true }); // let error: OwlError; // const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); // await expect(nextAppError(app)).resolves.toThrow("error occurred in onWillStart"); // await mountProm; // 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`
`; // val = 3; // setup() { // onWillPatch(() => { // throw new Error("boom"); // }); // onError((e) => (error = e)); // } // } // const root = await mount(Root, fixture, { test: true }); // root.val = 4; // let error: Error; // root.render(); // await nextTick(); // expect(error!).toBeDefined(); // expect(error!.message).toBe(`The following error occurred in onWillPatch: "boom"`); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(0); // }); // test("an error in patched call will reject the render promise", async () => { // class Root extends Component { // static template = xml`
`; // val = 3; // setup() { // onPatched(() => { // throw new Error("boom"); // }); // onError((e) => (error = e)); // } // } // const root = await mount(Root, fixture, { test: true }); // root.val = 4; // let error: Error; // root.render(); // await nextTick(); // expect(error!).toBeDefined(); // expect(error!.message).toBe(`The following error occurred in onPatched: "boom"`); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(0); // }); // test("a rendering error in a sub component will reject the mount promise", async () => { // // we do not catch error in willPatch anymore // class Child extends Component { // static template = xml`
`; // } // class Parent extends Component { // static template = xml`
`; // static components = { Child }; // } // const app = new App(Parent); // let error: OwlError; // const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); // await expect(nextAppError(app)).resolves.toThrow("error occured in the owl lifecycle"); // await mountProm; // expect(error!).toBeDefined(); // expect(error!.cause).toBeDefined(); // const regexp = // /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g; // expect(error!.cause.message).toMatch(regexp); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(1); // }); // test("a rendering error will reject the render promise", async () => { // class Root extends Component { // static template = xml`
`; // flag = false; // setup() { // onError(({ cause }) => (error = cause)); // } // } // const root = await mount(Root, fixture); // expect(fixture.innerHTML).toBe("
"); // root.flag = true; // let error: Error; // root.render(); // await nextTick(); // expect(error!).toBeDefined(); // const regexp = // /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g; // expect(error!.message).toMatch(regexp); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(0); // }); // test("a rendering error will reject the render promise (with sub components)", async () => { // class Child extends Component { // static template = xml``; // } // class Parent extends Component { // static template = xml`
`; // static components = { Child }; // } // const app = new App(Parent); // let error: OwlError; // const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); // await expect(nextAppError(app)).resolves.toThrow("error occured in the owl lifecycle"); // await mountProm; // expect(error!).toBeDefined(); // expect(error!.cause).toBeDefined(); // const regexp = // /Cannot read properties of undefined \(reading 'y'\)|Cannot read property 'y' of undefined/g; // expect(error!.cause.message).toMatch(regexp); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(1); // }); // test("errors in mounted and in willUnmount", async () => { // class Example extends Component { // static template = xml`
`; // val: any; // setup() { // onMounted(() => { // throw new Error("Error in mounted"); // this.val = { foo: "bar" }; // }); // onWillUnmount(() => { // console.log(this.val.foo); // }); // } // } // const app = new App(Example, { test: true }); // let error: OwlError; // const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); // await expect(nextAppError(app)).resolves.toThrow("error occurred in onMounted"); // await mountProm; // expect(error!.message).toBe(`The following error occurred in onMounted: "Error in mounted"`); // // 1 additional error is logged because the destruction of the app causes // // the onWillUnmount hook to be called and to fail // expect(mockConsoleError).toBeCalledTimes(1); // expect(mockConsoleWarn).toBeCalledTimes(1); // }); // test("errors in rerender", async () => { // class Example extends Component { // static template = xml`
`; // state: any = { a: { b: 1 } }; // } // const root = await mount(Example, fixture); // expect(fixture.innerHTML).toBe("
1
"); // root.state = "boom"; // root.render(); // await expect(nextAppError(root.__owl__.app)).resolves.toThrow( // "error occured in the owl lifecycle" // ); // expect(fixture.innerHTML).toBe(""); // expect(mockConsoleWarn).toBeCalledTimes(1); // }); // }); // describe("can catch errors", () => { // test("can catch an error in a component render function", async () => { // class ErrorComponent extends Component { // static template = xml`
hey
`; // } // class ErrorBoundary extends Component { // static template = xml` //
// Error handled // //
`; // state = useState({ error: false }); // setup() { // onError(() => (this.state.error = true)); // } // } // class App extends Component { // static template = xml` //
// //
`; // state = useState({ flag: false }); // static components = { ErrorBoundary, ErrorComponent }; // } // const app = await mount(App, fixture); // expect(fixture.innerHTML).toBe("
heyfalse
"); // app.state.flag = true; // await nextTick(); // expect(fixture.innerHTML).toBe("
Error handled
"); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(0); // }); // test("can catch an error in onmounted", async () => { // class ErrorComponent extends Component { // static template = xml`
Error!!!
`; // setup() { // useLogLifecycle(); // onMounted(() => { // throw new Error("error"); // }); // } // } // class PerfectComponent extends Component { // static template = xml`
perfect
`; // setup() { // useLogLifecycle(); // } // } // class Main extends Component { // static template = xml`Main`; // component: any; // state: any; // setup() { // this.state = useState({ ok: false }); // useLogLifecycle(); // this.component = ErrorComponent; // onError(() => { // this.component = PerfectComponent; // this.render(); // }); // } // } // const app = await mount(Main, fixture); // expect(steps.splice(0)).toMatchInlineSnapshot(` // Array [ // "Main:setup", // "Main:willStart", // "Main:willRender", // "Main:rendered", // "Main:mounted", // ] // `); // expect(fixture.innerHTML).toBe("Main"); // (app as any).state.ok = true; // await nextTick(); // expect(fixture.innerHTML).toBe("Main
Error!!!
"); // expect(steps.splice(0)).toMatchInlineSnapshot(` // Array [ // "Main:willRender", // "ErrorComponent:setup", // "ErrorComponent:willStart", // "Main:rendered", // "ErrorComponent:willRender", // "ErrorComponent:rendered", // "Main:willPatch", // "ErrorComponent:mounted", // "Main:willRender", // "PerfectComponent:setup", // "PerfectComponent:willStart", // "Main:rendered", // ] // `); // await nextTick(); // expect(steps.splice(0)).toMatchInlineSnapshot(` // Array [ // "PerfectComponent:willRender", // "PerfectComponent:rendered", // "Main:willPatch", // "ErrorComponent:willUnmount", // "ErrorComponent:willDestroy", // "PerfectComponent:mounted", // "Main:patched", // ] // `); // expect(fixture.innerHTML).toBe("Main
perfect
"); // }); // test("calling a hook outside setup should crash", async () => { // class Root extends Component { // static template = xml``; // state = useState({ value: 1 }); // setup() { // onWillStart(() => { // this.state = useState({ value: 2 }); // }); // } // } // const app = new App(Root, { test: true }); // let error: OwlError; // const crashProm = expect(nextAppError(app)).resolves.toThrow("error occurred in onWillStart"); // await app.mount(fixture).catch((e: Error) => (error = e)); // await crashProm; // expect(error!.message).toBe( // `The following error occurred in onWillStart: "No active component (a hook function should only be called in 'setup')"` // ); // }); // test("Errors have the right cause", async () => { // const err = new Error("test error"); // class Root extends Component { // static template = xml``; // state = useState({ value: 1 }); // setup() { // onMounted(() => { // throw err; // }); // } // } // const app = new App(Root, { test: true }); // let error: OwlError; // const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); // await expect(nextAppError(app)).resolves.toThrow("error occurred in onMounted"); // await mountProm; // expect(error!.message).toBe(`The following error occurred in onMounted: "test error"`); // expect(error!.cause).toBe(err); // }); // test("Errors in owl lifecycle are wrapped in dev mode: async hook", async () => { // const err = new Error("test error"); // class Root extends Component { // static template = xml``; // state = useState({ value: 1 }); // setup() { // onWillStart(async () => { // await nextMicroTick(); // throw err; // }); // } // } // const app = new App(Root, { test: true }); // let error: OwlError; // const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); // await expect(nextAppError(app)).resolves.toThrow("error occurred in onWillStart"); // await mountProm; // expect(error!.message).toBe(`The following error occurred in onWillStart: "test error"`); // expect(error!.cause).toBe(err); // }); // test("Errors in owl lifecycle are wrapped outside dev mode: sync hook", async () => { // const err = new Error("test error"); // class Root extends Component { // static template = xml``; // state = useState({ value: 1 }); // setup() { // onMounted(() => { // throw err; // }); // } // } // const app = new App(Root); // let error: OwlError; // const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); // await expect(nextAppError(app)).resolves.toThrow("error occured in the owl lifecycle"); // await mountProm; // expect(error!.message).toBe( // `An error occured in the owl lifecycle (see this Error's "cause" property)` // ); // expect(error!.cause).toBe(err); // }); // test("Errors in owl lifecycle are wrapped out of dev mode: async hook", async () => { // const err = new Error("test error"); // class Root extends Component { // static template = xml``; // state = useState({ value: 1 }); // setup() { // onWillStart(async () => { // await nextMicroTick(); // throw err; // }); // } // } // const app = new App(Root); // let error: OwlError; // const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); // await expect(nextAppError(app)).resolves.toThrow("error occured in the owl lifecycle"); // await mountProm; // expect(error!.message).toBe( // `An error occured in the owl lifecycle (see this Error's "cause" property)` // ); // expect(error!.cause).toBe(err); // }); // test("Thrown values that are not errors are wrapped in dev mode", async () => { // class Root extends Component { // static template = xml``; // state = useState({ value: 1 }); // setup() { // onMounted(() => { // throw "This is not an error"; // }); // } // } // const app = new App(Root, { test: true }); // let error: OwlError; // const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); // await expect(nextAppError(app)).resolves.toThrow("not an Error was thrown in onMounted"); // await mountProm; // expect(error!.message).toBe( // `Something that is not an Error was thrown in onMounted (see this Error's "cause" property)` // ); // expect(error!.cause).toBe("This is not an error"); // }); // test("Thrown values that are not errors are wrapped outside dev mode", async () => { // class Root extends Component { // static template = xml``; // state = useState({ value: 1 }); // setup() { // onMounted(() => { // throw "This is not an error"; // }); // } // } // const app = new App(Root); // let error: OwlError; // const mountProm = app.mount(fixture).catch((e: Error) => (error = e)); // await expect(nextAppError(app)).resolves.toThrow("error occured in the owl lifecycle"); // await mountProm; // expect(error!.message).toBe( // `An error occured in the owl lifecycle (see this Error's "cause" property)` // ); // expect(error!.cause).toBe("This is not an error"); // }); // test("can catch an error in the initial call of a component render function (parent mounted)", async () => { // class ErrorComponent extends Component { // static template = xml`
hey
`; // } // class ErrorBoundary extends Component { // static template = xml` //
// Error handled // //
`; // state = useState({ error: false }); // setup() { // onError(() => { // this.state.error = true; // }); // } // } // class App extends Component { // static template = xml` //
// //
`; // static components = { ErrorBoundary, ErrorComponent }; // } // await mount(App, fixture); // expect(fixture.innerHTML).toBe("
Error handled
"); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(0); // }); // test("can catch an error in the initial call of a component render function (parent updated)", async () => { // class ErrorComponent extends Component { // static template = xml`
hey
`; // } // class ErrorBoundary extends Component { // static template = xml` //
// Error handled // //
`; // state = useState({ error: false }); // setup() { // onError(() => (this.state.error = true)); // } // } // class App extends Component { // static template = xml` //
// //
`; // state = useState({ flag: false }); // static components = { ErrorBoundary, ErrorComponent }; // } // const app = await mount(App, fixture); // app.state.flag = true; // await nextTick(); // expect(fixture.innerHTML).toBe("
Error handled
"); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(0); // }); // test("can catch an error in the constructor call of a component render function", async () => { // class ErrorComponent extends Component { // static template = xml`
Some text
`; // setup() { // throw new Error("NOOOOO"); // } // } // class ErrorBoundary extends Component { // static template = xml`
// Error handled // //
`; // state = useState({ error: false }); // setup() { // onError(() => (this.state.error = true)); // } // } // class App extends Component { // static template = xml`
// //
`; // static components = { ErrorBoundary, ErrorComponent }; // } // await mount(App, fixture); // expect(fixture.innerHTML).toBe("
Error handled
"); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(0); // }); // test("can catch an error in the constructor call of a component render function 2", async () => { // class ClassicCompoent extends Component { // static template = xml`
classic
`; // } // class ErrorComponent extends Component { // static template = xml`
Some text
`; // setup() { // throw new Error("NOOOOO"); // } // } // class ErrorBoundary extends Component { // static template = xml`
// Error handled // //
`; // state = useState({ error: false }); // setup() { // onError(() => (this.state.error = true)); // } // } // class App extends Component { // static template = xml`
// //
`; // static components = { ErrorBoundary, ErrorComponent, ClassicCompoent }; // } // await mount(App, fixture); // expect(fixture.innerHTML).toBe("
Error handled
"); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(0); // }); // test("can catch an error in the willStart call", async () => { // class ErrorComponent extends Component { // static template = xml`
Some text
`; // setup() { // onWillStart(async () => { // // we wait a little bit to be in a different stack frame // await nextTick(); // throw new Error("NOOOOO"); // }); // } // } // class ErrorBoundary extends Component { // static template = xml` //
// Error handled // //
`; // state = useState({ error: false }); // setup() { // onError(() => (this.state.error = true)); // } // } // class App extends Component { // static template = xml`
`; // static components = { ErrorBoundary, ErrorComponent }; // } // await mount(App, fixture); // expect(fixture.innerHTML).toBe("
Error handled
"); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(0); // }); // test("can catch an error origination from a child's willStart function", async () => { // class ClassicCompoent extends Component { // static template = xml`
classic
`; // } // class ErrorComponent extends Component { // static template = xml`
Some text
`; // setup() { // onWillStart(() => { // throw new Error("NOOOOO"); // }); // } // } // class ErrorBoundary extends Component { // static template = xml`
// Error handled // //
`; // state = useState({ error: false }); // setup() { // onError(() => (this.state.error = true)); // } // } // class App extends Component { // static template = xml`
// //
`; // static components = { ErrorBoundary, ErrorComponent, ClassicCompoent }; // } // await mount(App, fixture); // expect(fixture.innerHTML).toBe("
Error handled
"); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(0); // }); // test("can catch an error in the mounted call", async () => { // class ErrorComponent extends Component { // static template = xml`
Some text
`; // setup() { // useLogLifecycle(); // onMounted(() => { // logStep("boom"); // throw new Error("NOOOOO"); // }); // } // } // class ErrorBoundary extends Component { // static template = xml`
// Error handled // //
`; // state = useState({ error: false }); // setup() { // useLogLifecycle(); // onError(() => (this.state.error = true)); // } // } // class Root extends Component { // static template = xml`
// //
`; // static components = { ErrorBoundary, ErrorComponent }; // setup() { // useLogLifecycle(); // } // } // await mount(Root, fixture); // expect(fixture.innerHTML).toBe("
Error handled
"); // expect(steps.splice(0)).toMatchInlineSnapshot(` // Array [ // "Root:setup", // "Root:willStart", // "Root:willRender", // "ErrorBoundary:setup", // "ErrorBoundary:willStart", // "Root:rendered", // "ErrorBoundary:willRender", // "ErrorComponent:setup", // "ErrorComponent:willStart", // "ErrorBoundary:rendered", // "ErrorComponent:willRender", // "ErrorComponent:rendered", // "ErrorComponent:mounted", // "boom", // "ErrorBoundary:willRender", // "ErrorBoundary:rendered", // "ErrorBoundary:mounted", // "Root:mounted", // ] // `); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(0); // }); // test("can catch an error in the mounted call (in root component)", async () => { // class ErrorComponent extends Component { // static template = xml`
Some text
`; // setup() { // useLogLifecycle(); // onMounted(() => { // logStep("boom"); // throw new Error("NOOOOO"); // }); // } // } // class Root extends Component { // static template = xml`
// Error handled // //
`; // static components = { ErrorComponent }; // state = useState({ error: false }); // setup() { // useLogLifecycle(); // onError(() => (this.state.error = true)); // } // } // await mount(Root, fixture); // expect(fixture.innerHTML).toBe("
Error handled
"); // expect(steps.splice(0)).toMatchInlineSnapshot(` // Array [ // "Root:setup", // "Root:willStart", // "Root:willRender", // "ErrorComponent:setup", // "ErrorComponent:willStart", // "Root:rendered", // "ErrorComponent:willRender", // "ErrorComponent:rendered", // "ErrorComponent:mounted", // "boom", // "Root:willRender", // "Root:rendered", // "Root:mounted", // ] // `); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(0); // }); // test("can catch an error in the mounted call (in child of child)", async () => { // class Boom extends Component { // static template = xml`
Some text
`; // setup() { // useLogLifecycle(); // onMounted(() => { // logStep("boom"); // throw new Error("NOOOOO"); // }); // } // } // class C extends Component { // static template = xml`
// Error handled // //
`; // static components = { Boom }; // state = useState({ error: false }); // setup() { // useLogLifecycle(); // onError(() => (this.state.error = true)); // } // } // class B extends Component { // static template = xml`
`; // static components = { C }; // setup() { // useLogLifecycle(); // } // } // class A extends Component { // static template = xml``; // static components = { B }; // setup() { // useLogLifecycle(); // } // } // await mount(A, fixture); // expect(fixture.innerHTML).toBe("
Error handled
"); // expect(steps.splice(0)).toMatchInlineSnapshot(` // Array [ // "A:setup", // "A:willStart", // "A:willRender", // "B:setup", // "B:willStart", // "A:rendered", // "B:willRender", // "C:setup", // "C:willStart", // "B:rendered", // "C:willRender", // "Boom:setup", // "Boom:willStart", // "C:rendered", // "Boom:willRender", // "Boom:rendered", // "Boom:mounted", // "boom", // "C:willRender", // "C:rendered", // "C:mounted", // "B:mounted", // "A:mounted", // ] // `); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(0); // }); // test("error in mounted on a component with a sibling (properly mounted)", async () => { // class ErrorComponent extends Component { // static template = xml`
Some text
`; // setup() { // useLogLifecycle(); // onMounted(() => { // logStep("boom"); // throw new Error("NOOOOO"); // }); // } // } // class ErrorBoundary extends Component { // static template = xml`
// Error handled // //
`; // state = useState({ error: false }); // setup() { // useLogLifecycle(); // onError(() => (this.state.error = true)); // } // } // class OK extends Component { // static template = xml`OK`; // setup() { // useLogLifecycle(); // } // } // class Root extends Component { // static template = xml`
// // //
`; // static components = { ErrorBoundary, ErrorComponent, OK }; // setup() { // useLogLifecycle(); // } // } // await mount(Root, fixture); // expect(fixture.innerHTML).toBe("
OK
Error handled
"); // expect(steps.splice(0)).toMatchInlineSnapshot(` // Array [ // "Root:setup", // "Root:willStart", // "Root:willRender", // "OK:setup", // "OK:willStart", // "ErrorBoundary:setup", // "ErrorBoundary:willStart", // "Root:rendered", // "OK:willRender", // "OK:rendered", // "ErrorBoundary:willRender", // "ErrorComponent:setup", // "ErrorComponent:willStart", // "ErrorBoundary:rendered", // "ErrorComponent:willRender", // "ErrorComponent:rendered", // "ErrorComponent:mounted", // "boom", // "ErrorBoundary:willRender", // "ErrorBoundary:rendered", // "ErrorBoundary:mounted", // "OK:mounted", // "Root:mounted", // ] // `); // expect(mockConsoleError).toBeCalledTimes(0); // expect(mockConsoleWarn).toBeCalledTimes(0); // }); // test("can catch an error in the willPatch call", async () => { // class ErrorComponent extends Component { // static template = xml`
`; // setup() { // onWillPatch(() => { // throw new Error("NOOOOO"); // }); // } // } // class ErrorBoundary extends Component { // static template = xml` //
// Error handled // //
`; // state = useState({ error: false }); // setup() { // onError(() => (this.state.error = true)); // } // } // class App extends Component { // static template = xml` //
// // //
`; // state = useState({ message: "abc" }); // static components = { ErrorBoundary, ErrorComponent }; // } // const app = await mount(App, fixture); // expect(fixture.innerHTML).toBe("
abc
abc
"); // app.state.message = "def"; // await nextTick(); // await nextTick(); // await nextTick(); // expect(fixture.innerHTML).toBe("
def
Error handled
"); // expect(mockConsoleWarn).toBeCalledTimes(0); // }); // test("catchError in catchError", async () => { // class Boom extends Component { // static template = xml`
`; // } // class Child extends Component { // static template = xml` //
// //
`; // static components = { Boom }; // setup() { // onError((error) => { // throw error; // }); // } // } // class Parent extends Component { // static template = xml` //
// Error // // // //
`; // static components = { Child }; // error: any = false; // setup() { // onError((error) => { // this.error = error; // this.render(); // }); // } // } // await mount(Parent, fixture); // expect(fixture.innerHTML).toBe("
Error
"); // expect(mockConsoleWarn).toBeCalledTimes(0); // }); // test("onError in class inheritance is not called if no rethrown", async () => { // const steps: string[] = []; // class Abstract extends Component { // static template = xml`
// // // // // // //
`; // state: any; // setup() { // this.state = useState({}); // onError(() => { // steps.push("Abstract onError"); // this.state.error = "Abstract"; // }); // } // } // class Concrete extends Abstract { // setup() { // super.setup(); // onError(() => { // steps.push("Concrete onError"); // this.state.error = "Concrete"; // }); // } // } // class Parent extends Component { // static components = { Concrete }; // static template = xml``; // } // await mount(Parent, fixture); // expect(steps).toStrictEqual(["Concrete onError"]); // expect(fixture.innerHTML).toBe("
Concrete
"); // expect(mockConsoleWarn).toBeCalledTimes(0); // }); // test("onError in class inheritance is called if rethrown", async () => { // const steps: string[] = []; // class Abstract extends Component { // static template = xml`
// // // // // // //
`; // state: any; // setup() { // this.state = useState({}); // onError(() => { // steps.push("Abstract onError"); // this.state.error = "Abstract"; // }); // } // } // class Concrete extends Abstract { // setup() { // super.setup(); // onError((error) => { // steps.push("Concrete onError"); // this.state.error = "Concrete"; // throw error; // }); // } // } // class Parent extends Component { // static components = { Concrete }; // static template = xml``; // } // await mount(Parent, fixture); // expect(steps).toStrictEqual(["Concrete onError", "Abstract onError"]); // expect(fixture.innerHTML).toBe("
Abstract
"); // expect(mockConsoleWarn).toBeCalledTimes(0); // }); // test("catching error, rethrow, render parent -- a main component loop implementation", async () => { // let parentState: any; // class ErrorComponent extends Component { // static template = xml`
`; // setup() { // throw new Error("My Error"); // } // } // class Child extends Component { // static template = xml``; // static components = { ErrorComponent }; // setup() { // onError((error) => { // throw error; // }); // } // } // class Sibling extends Component { // static template = xml`
Sibling
`; // } // class ErrorHandler extends Component { // static template = xml``; // setup() { // onError(() => { // this.props.onError(); // Promise.resolve().then(() => { // parentState.cps[2] = { // id: 2, // Comp: Sibling, // }; // }); // }); // } // } // class Parent extends Component { // static template = xml` // // // // // `; // static components = { ErrorHandler }; // state: any = useState({ // cps: {}, // }); // setup() { // parentState = this.state; // } // cleanUp(id: number) { // delete this.state.cps[id]; // } // } // await mount(Parent, fixture); // parentState.cps[1] = { id: 1, Comp: Child }; // await nextMicroTick(); // expect(fixture.innerHTML).toBe(""); // await nextTick(); // expect(fixture.innerHTML).toBe("
Sibling
"); // }); // test("catching in child makes parent render", async () => { // class Child extends Component { // static template = xml`
`; // } // class ErrorComp extends Component { // static template = xml`
`; // setup() { // throw new Error("Error Component"); // } // } // class Catch extends Component { // static template = xml``; // setup() { // onError(({ cause }) => { // this.props.onError(cause); // }); // } // } // const steps: any[] = []; // class Parent extends Component { // static components = { Catch }; // static template = xml` // // // // // // `; // elements: any = {}; // onError(id: any, error: Error) { // steps.push(error.message); // delete this.elements[id]; // this.elements[2] = Child; // this.render(); // } // } // const parent = await mount(Parent, fixture); // expect(fixture.innerHTML).toBe(""); // parent.elements[1] = ErrorComp; // parent.render(); // await nextTick(); // expect(fixture.innerHTML).toBe("
Child 2
"); // expect(steps).toEqual(["Error Component"]); // }); // test("an error in onWillDestroy", async () => { // class Child extends Component { // static template = xml`
abc
`; // setup() { // useLogLifecycle(); // onWillDestroy(() => { // throw new Error("boom"); // }); // } // } // class Parent extends Component { // static template = xml` // // `; // static components = { Child }; // state = useState({ value: 1, hasChild: true }); // setup() { // useLogLifecycle(); // onError(() => { // this.state.value++; // }); // } // } // const parent = await mount(Parent, fixture); // expect(fixture.innerHTML).toBe("1
abc
"); // expect(steps.splice(0)).toMatchInlineSnapshot(` // Array [ // "Parent:setup", // "Parent:willStart", // "Parent:willRender", // "Child:setup", // "Child:willStart", // "Parent:rendered", // "Child:willRender", // "Child:rendered", // "Child:mounted", // "Parent:mounted", // ] // `); // parent.state.hasChild = false; // await nextTick(); // await nextTick(); // await nextTick(); // await nextTick(); // expect(steps.splice(0)).toMatchInlineSnapshot(` // Array [ // "Parent:willRender", // "Parent:rendered", // "Parent:willPatch", // "Child:willUnmount", // "Child:willDestroy", // "Parent:patched", // "Parent:willRender", // "Parent:rendered", // "Parent:willPatch", // "Parent:patched", // ] // `); // expect(fixture.innerHTML).toBe("2"); // }); // test("an error in onWillDestroy, variation", async () => { // class Child extends Component { // static template = xml`
abc
`; // setup() { // useLogLifecycle(); // onWillDestroy(() => { // throw new Error("boom"); // }); // } // } // class Parent extends Component { // static template = xml` // // `; // static components = { Child }; // state = useState({ value: 1, hasChild: false }); // setup() { // useLogLifecycle(); // onError(() => { // this.state.value++; // }); // } // } // const parent = await mount(Parent, fixture); // expect(fixture.innerHTML).toBe("1"); // expect(steps.splice(0)).toMatchInlineSnapshot(` // Array [ // "Parent:setup", // "Parent:willStart", // "Parent:willRender", // "Parent:rendered", // "Parent:mounted", // ] // `); // parent.state.hasChild = true; // await nextMicroTick(); // await nextMicroTick(); // await nextMicroTick(); // await nextMicroTick(); // await nextMicroTick(); // expect(steps.splice(0)).toMatchInlineSnapshot(` // Array [ // "Parent:willRender", // "Child:setup", // "Child:willStart", // "Parent:rendered", // "Child:willRender", // "Child:rendered", // ] // `); // parent.state.hasChild = false; // await nextTick(); // expect(steps.splice(0)).toMatchInlineSnapshot(` // Array [ // "Parent:willRender", // "Parent:rendered", // "Child:willDestroy", // "Parent:willRender", // "Parent:rendered", // ] // `); // expect(fixture.innerHTML).toBe("1"); // await nextTick(); // expect(steps.splice(0)).toMatchInlineSnapshot(` // Array [ // "Parent:willPatch", // "Parent:patched", // ] // `); // expect(fixture.innerHTML).toBe("2"); // }); // test("error in onMounted, graceful recovery", async () => { // class Child extends Component { // static template = xml`abc`; // setup() { // useLogLifecycle(); // } // } // class OtherChild extends Component { // static template = xml`def`; // setup() { // useLogLifecycle(); // } // } // class Boom extends Component { // static template = xml`boom`; // setup() { // useLogLifecycle(); // onMounted(() => { // throw new Error("boom"); // }); // } // } // class Parent extends Component { // static template = xml`parent`; // static components = { Child, Boom }; // setup() { // useLogLifecycle(); // } // } // class Root extends Component { // static template = xml``; // component: any = Parent; // setup() { // useLogLifecycle(); // onError(() => { // logStep("error"); // this.component = OtherChild; // this.render(); // }); // } // } // await mount(Root, fixture); // expect(fixture.innerHTML).toBe("def"); // expect(steps.splice(0)).toMatchInlineSnapshot(` // Array [ // "Root:setup", // "Root:willStart", // "Root:willRender", // "Parent:setup", // "Parent:willStart", // "Root:rendered", // "Parent:willRender", // "Child:setup", // "Child:willStart", // "Boom:setup", // "Boom:willStart", // "Parent:rendered", // "Child:willRender", // "Child:rendered", // "Boom:willRender", // "Boom:rendered", // "Boom:mounted", // "error", // "Root:willRender", // "OtherChild:setup", // "OtherChild:willStart", // "Root:rendered", // "OtherChild:willRender", // "OtherChild:rendered", // "OtherChild:mounted", // "Root:mounted", // ] // `); // }); // test("error in onMounted, graceful recovery, variation", async () => { // class Child extends Component { // static template = xml`abc`; // setup() { // useLogLifecycle(); // } // } // class OtherChild extends Component { // static template = xml`def`; // setup() { // useLogLifecycle(); // } // } // class Boom extends Component { // static template = xml`boom`; // setup() { // useLogLifecycle(); // onMounted(() => { // throw new Error("boom"); // }); // } // } // class Parent extends Component { // static template = xml`parent`; // static components = { Child, Boom }; // setup() { // useLogLifecycle(); // } // } // class Root extends Component { // static template = xml`R`; // component: any = Parent; // state = useState({ gogogo: false }); // setup() { // useLogLifecycle(); // onError(() => { // logStep("error"); // this.component = OtherChild; // this.render(); // }); // } // } // const root = await mount(Root, fixture); // expect(fixture.innerHTML).toBe("R"); // // standard mounting process // expect(steps.splice(0)).toMatchInlineSnapshot(` // Array [ // "Root:setup", // "Root:willStart", // "Root:willRender", // "Root:rendered", // "Root:mounted", // ] // `); // root.state.gogogo = true; // await nextTick(); // expect(fixture.innerHTML).toBe("Rparentabcboom"); // // rerender, root creates sub components, it crashes, tries to recover // expect(steps.splice(0)).toMatchInlineSnapshot(` // Array [ // "Root:willRender", // "Parent:setup", // "Parent:willStart", // "Root:rendered", // "Parent:willRender", // "Child:setup", // "Child:willStart", // "Boom:setup", // "Boom:willStart", // "Parent:rendered", // "Child:willRender", // "Child:rendered", // "Boom:willRender", // "Boom:rendered", // "Root:willPatch", // "Boom:mounted", // "error", // "Root:willRender", // "OtherChild:setup", // "OtherChild:willStart", // "Root:rendered", // ] // `); // await nextTick(); // expect(fixture.innerHTML).toBe("Rdef"); // expect(steps.splice(0)).toMatchInlineSnapshot(` // Array [ // "OtherChild:willRender", // "OtherChild:rendered", // "Root:willPatch", // "Child:willDestroy", // "Boom:willUnmount", // "Boom:willDestroy", // "Parent:willDestroy", // "OtherChild:mounted", // "Root:patched", // ] // `); // }); // });