[IMP] component: validate mounting target at patch time

This commit is contained in:
Géry Debongnie
2022-01-31 16:57:58 +01:00
committed by aab-odoo
parent efaa186544
commit fa7a393f27
5 changed files with 47 additions and 10 deletions
+2 -10
View File
@@ -4,6 +4,7 @@ import { MountOptions } from "../component/fibers";
import { Scheduler } from "../component/scheduler";
import { TemplateSet, TemplateSetConfig } from "./template_set";
import { nodeErrorHandlers } from "../component/error_handling";
import { validateTarget } from "../utils";
// reimplement dev mode stuff see last change in 0f7a8289a6fb8387c3c1af41c6664b2a8448758f
@@ -48,22 +49,13 @@ export class App<
}
mount(target: HTMLElement, options?: MountOptions): Promise<Component<P, E> & InstanceType<T>> {
this.checkTarget(target);
validateTarget(target);
const node = this.makeNode(this.Root, this.props);
const prom = this.mountNode(node, target, options);
this.root = node;
return prom;
}
checkTarget(target: HTMLElement) {
if (!(target instanceof HTMLElement)) {
throw new Error("Cannot mount component: the target is not a valid DOM element");
}
if (!document.body.contains(target)) {
throw new Error("Cannot mount a component on a detached dom node");
}
}
makeNode(Component: ComponentConstructor, props: any): ComponentNode {
return new ComponentNode(Component, props, this);
}
+2
View File
@@ -1,4 +1,5 @@
import { BDom, mount } from "../blockdom";
import { validateTarget } from "../utils";
import type { ComponentNode } from "./component_node";
import { fibersInError, handleError } from "./error_handling";
import { STATUS } from "./status";
@@ -158,6 +159,7 @@ export class MountFiber extends RootFiber {
complete() {
let current: Fiber | undefined = this;
try {
validateTarget(this.target);
const node = this.node;
if (node.bdom) {
// this is a complicated situation: if we mount a fiber with an existing
+9
View File
@@ -24,6 +24,15 @@ export function batched(callback: Callback): Callback {
};
}
export function validateTarget(target: HTMLElement) {
if (!(target instanceof HTMLElement)) {
throw new Error("Cannot mount component: the target is not a valid DOM element");
}
if (!document.body.contains(target)) {
throw new Error("Cannot mount a component on a detached dom node");
}
}
export class EventBus extends EventTarget {
trigger(name: string, payload?: any) {
this.dispatchEvent(new CustomEvent(name, { detail: payload }));
@@ -81,6 +81,19 @@ exports[`basics a class component inside a class component, no external dom 2`]
}"
`;
exports[`basics a component cannot be mounted in a detached node (even if node is detached later 1`] = `
"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[`basics a component inside a component 1`] = `
"function anonymous(bdom, helpers
) {
+21
View File
@@ -171,6 +171,27 @@ describe("basics", () => {
expect(error!.message).toBe("Cannot mount a component on a detached dom node");
});
test("a component cannot be mounted in a detached node (even if node is detached later)", async () => {
const warn = console.warn;
console.warn = jest.fn();
class Test extends Component {
static template = xml`<div/>`;
}
let error: Error;
const prom = mount(Test, fixture);
await Promise.resolve();
fixture.remove();
try {
await prom;
} catch (e) {
error = e as Error;
}
expect(error!).toBeDefined();
expect(error!.message).toBe("Cannot mount a component on a detached dom node");
expect(console.warn).toBeCalledTimes(1);
console.warn = warn;
});
test("crashes if it cannot find a template", async () => {
class Test extends Component {
static template = "wrongtemplate";