mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] runtime: allow using any class as a type in props validation
Previously, we had a fixed whitelist for types that were allowed during props validation. The implementation however supports using arbitrary classes, and in practice it's desirable to do so, and already done when not using typescript (when using typescript, it will error if the class is not whitelisted), eg in Odoo, we use "Element" for the arch in the standard view props, but this causes all view controllers to fail type checking because it's not whitelisted. This commit simply replaces existing constructors by a generic constructor type, and adds a test with a validation success and a test with a validation failure.
This commit is contained in:
committed by
Géry Debongnie
parent
33dfeb1b41
commit
97b69f164f
@@ -1,16 +1,7 @@
|
|||||||
import { OwlError } from "../common/owl_error";
|
import { OwlError } from "../common/owl_error";
|
||||||
import { toRaw } from "./reactivity";
|
import { toRaw } from "./reactivity";
|
||||||
|
|
||||||
type BaseType =
|
type BaseType = { new (...args: any[]): any } | true | "*";
|
||||||
| typeof String
|
|
||||||
| typeof Boolean
|
|
||||||
| typeof Number
|
|
||||||
| typeof Date
|
|
||||||
| typeof Object
|
|
||||||
| typeof Array
|
|
||||||
| typeof Function
|
|
||||||
| true
|
|
||||||
| "*";
|
|
||||||
|
|
||||||
interface TypeInfo {
|
interface TypeInfo {
|
||||||
type?: TypeDescription;
|
type?: TypeDescription;
|
||||||
|
|||||||
@@ -167,6 +167,45 @@ exports[`props validation can specify that additional props are allowed (object)
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`props validation can use custom class as type 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
const comp1 = app.createComponent(\`Child\`, true, false, false, [\\"customObj\\"]);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
const props1 = {customObj: ctx['customObj']};
|
||||||
|
helpers.validateProps(\`Child\`, props1, this);
|
||||||
|
return comp1(props1, key + \`__1\`, node, this, null);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`props validation can use custom class as type 2`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
return text(ctx['props'].customObj.val);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`props validation can use custom class as type: validation failure 1`] = `
|
||||||
|
"function anonymous(app, bdom, helpers
|
||||||
|
) {
|
||||||
|
let { text, createBlock, list, multi, html, toggler, comment } = bdom;
|
||||||
|
const comp1 = app.createComponent(\`Child\`, true, false, false, [\\"customObj\\"]);
|
||||||
|
|
||||||
|
return function template(ctx, node, key = \\"\\") {
|
||||||
|
const props1 = {customObj: ctx['customObj']};
|
||||||
|
helpers.validateProps(\`Child\`, props1, this);
|
||||||
|
return comp1(props1, key + \`__1\`, node, this, null);
|
||||||
|
}
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`props validation can validate a prop with multiple types 1`] = `
|
exports[`props validation can validate a prop with multiple types 1`] = `
|
||||||
"function anonymous(app, bdom, helpers
|
"function anonymous(app, bdom, helpers
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -829,6 +829,52 @@ describe("props validation", () => {
|
|||||||
expect(error!).toBeDefined();
|
expect(error!).toBeDefined();
|
||||||
expect(error!.message).toBe("Invalid props for component 'Child': 'message' is missing");
|
expect(error!.message).toBe("Invalid props for component 'Child': 'message' is missing");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("can use custom class as type", async () => {
|
||||||
|
class CustomClass {
|
||||||
|
val = "hey";
|
||||||
|
}
|
||||||
|
class Child extends Component {
|
||||||
|
static props = { customObj: CustomClass };
|
||||||
|
static template = xml`<t t-esc="props.customObj.val"/>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static components = { Child };
|
||||||
|
static template = xml`<Child customObj="customObj" />`;
|
||||||
|
customObj = new CustomClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new App(Parent, { test: true });
|
||||||
|
await app.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("hey");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("can use custom class as type: validation failure", async () => {
|
||||||
|
class CustomClass {}
|
||||||
|
class Child extends Component {
|
||||||
|
static props = { customObj: CustomClass };
|
||||||
|
static template = xml`<div>hey</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static components = { Child };
|
||||||
|
static template = xml`<Child customObj="customObj" />`;
|
||||||
|
customObj = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new App(Parent, { test: true });
|
||||||
|
let error: OwlError | undefined;
|
||||||
|
const mountProm = app.mount(fixture).catch((e: Error) => (error = e));
|
||||||
|
await expect(nextAppError(app)).resolves.toThrow(
|
||||||
|
"Invalid props for component 'Child': 'customObj' is not a customclass"
|
||||||
|
);
|
||||||
|
await mountProm;
|
||||||
|
expect(error!).toBeDefined();
|
||||||
|
expect(error!.message).toBe(
|
||||||
|
"Invalid props for component 'Child': 'customObj' is not a customclass"
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|||||||
Reference in New Issue
Block a user