Skip to content

Commit fb7d25b

Browse files
kebeclibreged-odoo
authored andcommitted
[FIX] error_handling: onError works if handling happens between culprit and root
Have a component implementing onError that calls a callback from one of its parent. That parent should not be the original source of the rendering (a parent above is). Make the callback handle the error and trigger an render() Before this commit, the parent did not see it was handling a subtree in error, and did not have a chance to revert that error state in its rendering stack. After this commit, this flow works.
1 parent 5187f01 commit fb7d25b

File tree

3 files changed

+85
-0
lines changed

3 files changed

+85
-0
lines changed

src/runtime/error_handling.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ export function handleError(params: ErrorParams) {
5555
let current: Fiber | null = fiber;
5656
do {
5757
current.node.fiber = current;
58+
fibersInError.set(current, error);
5859
current = current.parent;
5960
} while (current);
6061

tests/components/__snapshots__/error_handling.test.ts.snap

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,52 @@ exports[`basics no component catching error lead to full app destruction 2`] = `
9494
}"
9595
`;
9696

97+
exports[`basics render from above on error -- handler is not a Root or MountFiber 1`] = `
98+
"function anonymous(app, bdom, helpers
99+
) {
100+
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
101+
const comp1 = app.createComponent(\`Parent\`, true, false, false, []);
102+
103+
return function template(ctx, node, key = \\"\\") {
104+
return comp1({}, key + \`__1\`, node, this, null);
105+
}
106+
}"
107+
`;
108+
109+
exports[`basics render from above on error -- handler is not a Root or MountFiber 2`] = `
110+
"function anonymous(app, bdom, helpers
111+
) {
112+
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
113+
const comp1 = app.createComponent(\`Boom\`, true, false, false, []);
114+
115+
let block1 = createBlock(\`<div><block-child-0/><block-child-1/></div>\`);
116+
117+
return function template(ctx, node, key = \\"\\") {
118+
let b2, b3;
119+
if (ctx['error']) {
120+
b2 = text(\`Error\`);
121+
} else {
122+
b3 = comp1({onError: (ctx['handleError']).bind(this)}, key + \`__1\`, node, this, null);
123+
}
124+
return block1([], [b2, b3]);
125+
}
126+
}"
127+
`;
128+
129+
exports[`basics render from above on error -- handler is not a Root or MountFiber 3`] = `
130+
"function anonymous(app, bdom, helpers
131+
) {
132+
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
133+
134+
let block1 = createBlock(\`<div><block-text-0/></div>\`);
135+
136+
return function template(ctx, node, key = \\"\\") {
137+
let txt1 = ctx['a'].b.c;
138+
return block1([txt1]);
139+
}
140+
}"
141+
`;
142+
97143
exports[`basics simple catchError 1`] = `
98144
"function anonymous(app, bdom, helpers
99145
) {

tests/components/error_handling.test.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,44 @@ function(app, bdom, helpers) {
233233
expect(mockConsoleError).toBeCalledTimes(0);
234234
expect(mockConsoleWarn).toBeCalledTimes(0);
235235
});
236+
237+
test("render from above on error -- handler is not a Root or MountFiber", async () => {
238+
class Boom extends Component {
239+
static template = xml`<div t-esc="a.b.c"/>`;
240+
setup() {
241+
onError((err) => {
242+
this.props.onError(err);
243+
});
244+
}
245+
}
246+
247+
class Parent extends Component {
248+
static template = xml`
249+
<div>
250+
<t t-if="error">Error</t>
251+
<t t-else="">
252+
<Boom onError.bind="handleError"/>
253+
</t>
254+
</div>`;
255+
static components = { Boom };
256+
257+
error: any = false;
258+
259+
handleError(err: Error) {
260+
this.error = err;
261+
this.render();
262+
}
263+
}
264+
265+
class GrandParent extends Component {
266+
static template: string = xml`<Parent />`;
267+
static components = { Parent };
268+
}
269+
await mount(GrandParent, fixture);
270+
expect(fixture.innerHTML).toBe("<div>Error</div>");
271+
expect(mockConsoleError).toBeCalledTimes(0);
272+
expect(mockConsoleWarn).toBeCalledTimes(0);
273+
});
236274
});
237275

238276
describe("errors and promises", () => {

0 commit comments

Comments
 (0)