[FIX] component: error_handling when an error is rethrown

This commit is contained in:
Lucas Perais (lpe)
2021-12-20 16:27:09 +01:00
committed by Aaron Bohy
parent bf9cceb56f
commit cc1eea0945
3 changed files with 148 additions and 4 deletions
+3 -4
View File
@@ -16,10 +16,6 @@ function _handleError(node: ComponentNode | null, error: any, isFirstRound = fal
const errorHandlers = nodeErrorHandlers.get(node);
if (errorHandlers) {
if (isFirstRound && fiber) {
fiber.root.counter--;
}
let stopped = false;
// execute in the opposite order
for (let i = errorHandlers.length - 1; i >= 0; i--) {
@@ -33,6 +29,9 @@ function _handleError(node: ComponentNode | null, error: any, isFirstRound = fal
}
if (stopped) {
if (isFirstRound && fiber) {
fiber.root.counter--;
}
return true;
}
}
@@ -705,6 +705,81 @@ exports[`can catch errors catchError in catchError 3`] = `
}"
`;
exports[`can catch errors catching error, rethrow, render parent -- a main component loop implementation 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { prepareList, capture, withKey } = helpers;
function slot3(ctx, node, key = \\"\\") {
let Comp5 = ctx['cp'].Comp;
return toggler(Comp5, component(Comp5, {}, key + \`__4\`, node, ctx));
}
return function template(ctx, node, key = \\"\\") {
ctx = Object.create(ctx);
const [k_block1, v_block1, l_block1, c_block1] = prepareList(Object.values(ctx['state'].cps));
for (let i1 = 0; i1 < l_block1; i1++) {
ctx[\`cp\`] = v_block1[i1];
let key1 = ctx['cp'].id;
const v1 = ctx['cp'];
const ctx2 = capture(ctx);
c_block1[i1] = withKey(component(\`ErrorHandler\`, {onError: ()=>this.cleanUp(v1.id),slots: {'default': {__render: slot3, __ctx: ctx2}}}, key + \`__6__\${key1}\`, node, ctx), key1);
}
return list(c_block1);
}
}"
`;
exports[`can catch errors catching error, rethrow, render parent -- a main component loop implementation 2`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let { callSlot } = helpers;
return function template(ctx, node, key = \\"\\") {
return callSlot(ctx, node, key, 'default', false, {});
}
}"
`;
exports[`can catch errors catching error, rethrow, render parent -- a main component loop implementation 3`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
return function template(ctx, node, key = \\"\\") {
return component(\`ErrorComponent\`, {}, key + \`__1\`, node, ctx);
}
}"
`;
exports[`can catch errors catching error, rethrow, render parent -- a main component loop implementation 4`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div/>\`);
return function template(ctx, node, key = \\"\\") {
return block1();
}
}"
`;
exports[`can catch errors catching error, rethrow, render parent -- a main component loop implementation 5`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
let block1 = createBlock(\`<div>Sibling</div>\`);
return function template(ctx, node, key = \\"\\") {
return block1();
}
}"
`;
exports[`can catch errors error in mounted on a component with a sibling (properly mounted) 1`] = `
"function anonymous(bdom, helpers
) {
+70
View File
@@ -13,6 +13,7 @@ import {
logStep,
makeTestFixture,
nextTick,
nextMicroTick,
snapshotEverything,
useLogLifecycle,
} from "../helpers";
@@ -963,4 +964,73 @@ describe("can catch errors", () => {
expect(fixture.innerHTML).toBe("<div>Abstract</div>");
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`<div />`;
setup() {
throw new Error("My Error");
}
}
class Child extends Component {
static template = xml`<ErrorComponent />`;
static components = { ErrorComponent };
setup() {
onError((error) => {
throw error;
});
}
}
class Sibling extends Component {
static template = xml`<div>Sibling</div>`;
}
class ErrorHandler extends Component {
static template = xml`<t t-slot="default" />`;
setup() {
onError(() => {
this.props.onError();
Promise.resolve().then(() => {
parentState.cps[2] = {
id: 2,
Comp: Sibling,
};
});
});
}
}
class Parent extends Component {
static template = xml`
<t t-foreach="Object.values(state.cps)" t-as="cp" t-key="cp.id">
<ErrorHandler onError="() => this.cleanUp(cp.id)">
<t t-component="cp.Comp" />
</ErrorHandler>
</t>`;
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("<div>Sibling</div>");
});
});