mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[FIX] runtime: properly handle error caused in mounted hook
If a component onMounted hook causes an error, and an error handler tries to reinitiate a new correct render, the fiber may be recycled. But the scheduler removes it from its own set of tasks, so it will be ignored. We simply check for this condition before removing it from the tasks set.
This commit is contained in:
@@ -320,6 +320,50 @@ exports[`can catch errors can catch an error in a component render function 3`]
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`can catch errors can catch an error in onmounted 1`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
const comp1 = app.createComponent(null, false, false, false, []);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
let b2, b3;
|
||||
b2 = text(\`Main\`);
|
||||
if (ctx['state'].ok) {
|
||||
const Comp1 = ctx['component'];
|
||||
b3 = toggler(Comp1, comp1({}, (Comp1).name + key + \`__1\`, node, this, Comp1));
|
||||
}
|
||||
return multi([b2, b3]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`can catch errors can catch an error in onmounted 3`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
let block1 = createBlock(\`<div>Error!!!</div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`can catch errors can catch an error in onmounted 4`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||
|
||||
let block1 = createBlock(\`<div>perfect</div>\`);
|
||||
|
||||
return function template(ctx, node, key = \\"\\") {
|
||||
return block1();
|
||||
}
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`can catch errors can catch an error in the constructor call of a component render function 1`] = `
|
||||
"function anonymous(app, bdom, helpers
|
||||
) {
|
||||
|
||||
@@ -564,6 +564,82 @@ describe("can catch errors", () => {
|
||||
expect(mockConsoleWarn).toBeCalledTimes(0);
|
||||
});
|
||||
|
||||
test("can catch an error in onmounted", async () => {
|
||||
class ErrorComponent extends Component {
|
||||
static template = xml`<div>Error!!!</div>`;
|
||||
setup() {
|
||||
useLogLifecycle();
|
||||
onMounted(() => {
|
||||
throw new Error("error");
|
||||
});
|
||||
}
|
||||
}
|
||||
class PerfectComponent extends Component {
|
||||
static template = xml`<div>perfect</div>`;
|
||||
setup() {
|
||||
useLogLifecycle();
|
||||
}
|
||||
}
|
||||
class Main extends Component {
|
||||
static template = xml`Main<t t-if="state.ok" t-component="component"/>`;
|
||||
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<div>Error!!!</div>");
|
||||
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<div>perfect</div>");
|
||||
});
|
||||
|
||||
test("calling a hook outside setup should crash", async () => {
|
||||
class Root extends Component {
|
||||
static template = xml`<t t-esc="state.value"/>`;
|
||||
|
||||
Reference in New Issue
Block a user