[FIX] component: propagate errors to caller

Errors from mounted/willPatch/patched should be returned to caller
(either mount or render functions)

part of #410
This commit is contained in:
Géry Debongnie
2019-11-18 14:15:53 +01:00
committed by aab-odoo
parent 94a595ef5b
commit bd39797f17
6 changed files with 105 additions and 28 deletions
+3 -7
View File
@@ -444,13 +444,9 @@ export class Component<T extends Env, Props extends {}> {
} }
__owl__.isMounted = true; __owl__.isMounted = true;
__owl__.currentFiber = null; __owl__.currentFiber = null;
try { this.mounted();
this.mounted(); if (__owl__.mountedCB) {
if (__owl__.mountedCB) { __owl__.mountedCB();
__owl__.mountedCB();
}
} catch (e) {
console.error(e); // TODO : add a test
} }
} }
+10 -18
View File
@@ -196,16 +196,12 @@ export class Fiber {
this._walk(doWork); this._walk(doWork);
let component: Component<any, any> = this.component; let component: Component<any, any> = this.component;
const patchLen = patchQueue.length; const patchLen = patchQueue.length;
try { for (let i = 0; i < patchLen; i++) {
for (let i = 0; i < patchLen; i++) { component = patchQueue[i].component;
component = patchQueue[i].component; if (component.__owl__.willPatchCB) {
if (component.__owl__.willPatchCB) { component.__owl__.willPatchCB();
component.__owl__.willPatchCB();
}
component.willPatch();
} }
} catch (e) { component.willPatch();
console.error(e);
} }
for (let i = 0; i < patchLen; i++) { for (let i = 0; i < patchLen; i++) {
const fiber = patchQueue[i]; const fiber = patchQueue[i];
@@ -213,16 +209,12 @@ export class Fiber {
component.__patch(fiber.vnode); component.__patch(fiber.vnode);
component.__owl__.currentFiber = null; component.__owl__.currentFiber = null;
} }
try { for (let i = patchLen - 1; i >= 0; i--) {
for (let i = patchLen - 1; i >= 0; i--) { component = patchQueue[i].component;
component = patchQueue[i].component; component.patched();
component.patched(); if (component.__owl__.patchedCB) {
if (component.__owl__.patchedCB) { component.__owl__.patchedCB();
component.__owl__.patchedCB();
}
} }
} catch (e) {
console.error(e);
} }
} }
+5 -1
View File
@@ -59,7 +59,11 @@ export class Scheduler {
} }
if (task.fiber.counter === 0) { if (task.fiber.counter === 0) {
if (!task.fiber.error) { if (!task.fiber.error) {
task.fiber.complete(); try {
task.fiber.complete();
} catch (e) {
task.fiber.handleError(e);
}
} }
task.callback(); task.callback();
return false; return false;
+1 -1
View File
@@ -164,7 +164,7 @@ const TOKENIZERS = [
tokenizeNumber, tokenizeNumber,
tokenizeOperator, tokenizeOperator,
tokenizeSymbol, tokenizeSymbol,
tokenizeStatic, tokenizeStatic
]; ];
/** /**
+84
View File
@@ -4646,6 +4646,90 @@ describe("component error handling (catchError)", () => {
console.error = consoleError; console.error = consoleError;
}); });
test("an error in mounted call will reject the mount promise", async () => {
const consoleError = console.error;
console.error = jest.fn(() => {});
class App extends Component<any, any> {
static template = xml`<div>abc</div>`;
mounted() {
throw new Error("boom");
}
}
const app = new App();
let error;
try {
await app.mount(fixture);
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toBe("boom");
expect(fixture.innerHTML).toBe("");
expect(console.error).toBeCalledTimes(0);
console.error = consoleError;
});
test("an error in willPatch call will reject the render promise", async () => {
const consoleError = console.error;
console.error = jest.fn(() => {});
class App extends Component<any, any> {
static template = xml`<div><t t-esc="val"/></div>`;
val = 3;
willPatch() {
throw new Error("boom");
}
}
const app = new App();
await app.mount(fixture);
app.val = 4;
let error;
try {
await app.render();
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toBe("boom");
expect(fixture.innerHTML).toBe("");
expect(console.error).toBeCalledTimes(0);
console.error = consoleError;
});
test("an error in patched call will reject the render promise", async () => {
const consoleError = console.error;
console.error = jest.fn(() => {});
class App extends Component<any, any> {
static template = xml`<div><t t-esc="val"/></div>`;
val = 3;
patched() {
throw new Error("boom");
}
}
const app = new App();
await app.mount(fixture);
app.val = 4;
let error;
try {
await app.render();
} catch (e) {
error = e;
}
expect(error).toBeDefined();
expect(error.message).toBe("boom");
expect(fixture.innerHTML).toBe("");
expect(console.error).toBeCalledTimes(0);
console.error = consoleError;
});
test("a rendering error in a sub component will reject the mount promise", async () => { test("a rendering error in a sub component will reject the mount promise", async () => {
const consoleError = console.error; const consoleError = console.error;
console.error = jest.fn(() => {}); console.error = jest.fn(() => {});
+2 -1
View File
@@ -44,7 +44,8 @@ describe("tokenizer", () => {
{ type: "OPERATOR", value: "!=" } { type: "OPERATOR", value: "!=" }
]); ]);
expect(tokenize("typeof a")).toEqual([ expect(tokenize("typeof a")).toEqual([
{"type": "OPERATOR", "value": "typeof "}, {"type": "SYMBOL", "value": "a"} { type: "OPERATOR", value: "typeof " },
{ type: "SYMBOL", value: "a" }
]); ]);
}); });