mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[IMP] component: do not set props in root components
Also remove props from Fiber, as it was not useful anymore.
This commit is contained in:
committed by
Géry Debongnie
parent
0addca63a0
commit
7d249d6f09
@@ -114,23 +114,17 @@ export class Component<T extends Env, Props extends {}> {
|
|||||||
constructor(parent?: Component<T, any>, props?: Props) {
|
constructor(parent?: Component<T, any>, props?: Props) {
|
||||||
Component.current = this;
|
Component.current = this;
|
||||||
|
|
||||||
|
const id: number = nextId++;
|
||||||
|
let depth;
|
||||||
|
if (parent) {
|
||||||
const defaultProps = (<any>this.constructor).defaultProps;
|
const defaultProps = (<any>this.constructor).defaultProps;
|
||||||
if (defaultProps) {
|
if (defaultProps) {
|
||||||
props = this.__applyDefaultProps(props, defaultProps);
|
props = this.__applyDefaultProps(props, defaultProps);
|
||||||
}
|
}
|
||||||
// is this a good idea?
|
this.props = <Props>props;
|
||||||
// Pro: if props is empty, we can create easily a component
|
|
||||||
// Con: this is not really safe
|
|
||||||
// Pro: but creating component (by a template) is always unsafe anyway
|
|
||||||
this.props = <Props>props || <Props>{};
|
|
||||||
if (QWeb.dev) {
|
if (QWeb.dev) {
|
||||||
QWeb.utils.validateProps(this.constructor, this.props);
|
QWeb.utils.validateProps(this.constructor, this.props);
|
||||||
}
|
}
|
||||||
|
|
||||||
const id: number = nextId++;
|
|
||||||
|
|
||||||
let depth;
|
|
||||||
if (parent) {
|
|
||||||
this.env = parent.env;
|
this.env = parent.env;
|
||||||
const __powl__ = parent.__owl__;
|
const __powl__ = parent.__owl__;
|
||||||
__powl__.children[id] = this;
|
__powl__.children[id] = this;
|
||||||
@@ -138,6 +132,7 @@ export class Component<T extends Env, Props extends {}> {
|
|||||||
} else {
|
} else {
|
||||||
// we are the root component
|
// we are the root component
|
||||||
this.env = config.env as T;
|
this.env = config.env as T;
|
||||||
|
this.props = undefined as unknown as Props;
|
||||||
this.env.qweb.on("update", this, () => {
|
this.env.qweb.on("update", this, () => {
|
||||||
if (this.__owl__.isMounted) {
|
if (this.__owl__.isMounted) {
|
||||||
this.render(true);
|
this.render(true);
|
||||||
@@ -287,7 +282,7 @@ export class Component<T extends Env, Props extends {}> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const fiber = new Fiber(null, this, this.props, undefined, undefined, false);
|
const fiber = new Fiber(null, this, undefined, undefined, false);
|
||||||
scheduler.addFiber(fiber, err => {
|
scheduler.addFiber(fiber, err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
@@ -339,7 +334,7 @@ export class Component<T extends Env, Props extends {}> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const fiber = new Fiber(null, this, this.props, undefined, undefined, force);
|
const fiber = new Fiber(null, this, undefined, undefined, force);
|
||||||
scheduler.addFiber(fiber.root, err => {
|
scheduler.addFiber(fiber.root, err => {
|
||||||
if (err) {
|
if (err) {
|
||||||
reject(err);
|
reject(err);
|
||||||
@@ -488,7 +483,7 @@ export class Component<T extends Env, Props extends {}> {
|
|||||||
const shouldUpdate = parentFiber.force || this.shouldUpdate(nextProps);
|
const shouldUpdate = parentFiber.force || this.shouldUpdate(nextProps);
|
||||||
if (shouldUpdate) {
|
if (shouldUpdate) {
|
||||||
const __owl__ = this.__owl__;
|
const __owl__ = this.__owl__;
|
||||||
const fiber = new Fiber(parentFiber, this, this.props, scope, vars, parentFiber.force);
|
const fiber = new Fiber(parentFiber, this, scope, vars, parentFiber.force);
|
||||||
if (!parentFiber.child) {
|
if (!parentFiber.child) {
|
||||||
parentFiber.child = fiber;
|
parentFiber.child = fiber;
|
||||||
} else {
|
} else {
|
||||||
@@ -532,7 +527,7 @@ export class Component<T extends Env, Props extends {}> {
|
|||||||
* parent template.
|
* parent template.
|
||||||
*/
|
*/
|
||||||
__prepare(parentFiber: Fiber, scope: any, vars: any, previousSibling?: Fiber | null) {
|
__prepare(parentFiber: Fiber, scope: any, vars: any, previousSibling?: Fiber | null) {
|
||||||
const fiber = new Fiber(parentFiber, this, this.props, scope, vars, parentFiber.force);
|
const fiber = new Fiber(parentFiber, this, scope, vars, parentFiber.force);
|
||||||
fiber.shouldPatch = false;
|
fiber.shouldPatch = false;
|
||||||
if (!parentFiber.child) {
|
if (!parentFiber.child) {
|
||||||
parentFiber.child = fiber;
|
parentFiber.child = fiber;
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ export class Fiber {
|
|||||||
|
|
||||||
scope: any;
|
scope: any;
|
||||||
vars: any;
|
vars: any;
|
||||||
props: any;
|
|
||||||
|
|
||||||
component: Component<any, any>;
|
component: Component<any, any>;
|
||||||
vnode: VNode | null = null;
|
vnode: VNode | null = null;
|
||||||
@@ -56,11 +55,10 @@ export class Fiber {
|
|||||||
|
|
||||||
error?: Error;
|
error?: Error;
|
||||||
|
|
||||||
constructor(parent: Fiber | null, component: Component<any, any>, props, scope, vars, force) {
|
constructor(parent: Fiber | null, component: Component<any, any>, scope, vars, force) {
|
||||||
this.force = force;
|
this.force = force;
|
||||||
this.scope = scope;
|
this.scope = scope;
|
||||||
this.vars = vars;
|
this.vars = vars;
|
||||||
this.props = props;
|
|
||||||
this.component = component;
|
this.component = component;
|
||||||
|
|
||||||
this.root = parent ? parent.root : this;
|
this.root = parent ? parent.root : this;
|
||||||
|
|||||||
@@ -1036,7 +1036,7 @@ exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument
|
|||||||
var h = this.h;
|
var h = this.h;
|
||||||
let c1 = [], p1 = {key:1};
|
let c1 = [], p1 = {key:1};
|
||||||
var vn1 = h('div', p1, c1);
|
var vn1 = h('div', p1, c1);
|
||||||
var _2 = context['props'].items;
|
var _2 = context['items'];
|
||||||
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
if (!_2) { throw new Error('QWeb error: Invalid loop expression')}
|
||||||
var _3 = _4 = _2;
|
var _3 = _4 = _2;
|
||||||
if (!(_2 instanceof Array)) {
|
if (!(_2 instanceof Array)) {
|
||||||
|
|||||||
@@ -72,9 +72,9 @@ class WidgetA extends Widget {
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
describe("basic widget properties", () => {
|
describe("basic widget properties", () => {
|
||||||
test("props is properly defined", async () => {
|
test("props is not defined on root components", async () => {
|
||||||
const widget = new Widget();
|
const widget = new Widget();
|
||||||
expect(widget.props).toEqual({});
|
expect(widget.props).toBe(undefined);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("has no el after creation", async () => {
|
test("has no el after creation", async () => {
|
||||||
@@ -1645,10 +1645,11 @@ describe("props evaluation ", () => {
|
|||||||
class Parent extends Widget {
|
class Parent extends Widget {
|
||||||
static components = { child: Child };
|
static components = { child: Child };
|
||||||
get greetings() {
|
get greetings() {
|
||||||
return `hello ${this.props.name}`;
|
const name = "aaron";
|
||||||
|
return `hello ${name}`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const widget = new Parent(undefined, { name: "aaron" });
|
const widget = new Parent();
|
||||||
await widget.mount(fixture);
|
await widget.mount(fixture);
|
||||||
expect(fixture.innerHTML).toBe("<div><span>hello aaron</span></div>");
|
expect(fixture.innerHTML).toBe("<div><span>hello aaron</span></div>");
|
||||||
});
|
});
|
||||||
@@ -2336,30 +2337,27 @@ describe("random stuff/miscellaneous", () => {
|
|||||||
|
|
||||||
test("t-on with handler bound to dynamic argument on a t-foreach", async () => {
|
test("t-on with handler bound to dynamic argument on a t-foreach", async () => {
|
||||||
expect.assertions(3);
|
expect.assertions(3);
|
||||||
env.qweb.addTemplates(`
|
|
||||||
<templates>
|
|
||||||
<div t-name="ParentWidget">
|
|
||||||
<t t-foreach="props.items" t-as="item">
|
|
||||||
<Child t-key="item" t-on-ev="onEv(item)"/>
|
|
||||||
</t>
|
|
||||||
</div>
|
|
||||||
</templates>
|
|
||||||
`);
|
|
||||||
const items = [1, 2, 3, 4];
|
|
||||||
|
|
||||||
class Child extends Widget {}
|
class Child extends Widget {}
|
||||||
class ParentWidget extends Widget {
|
class ParentWidget extends Widget {
|
||||||
|
static template = xml`
|
||||||
|
<div>
|
||||||
|
<t t-foreach="items" t-as="item">
|
||||||
|
<Child t-key="item" t-on-ev="onEv(item)"/>
|
||||||
|
</t>
|
||||||
|
</div>`;
|
||||||
static components = { Child };
|
static components = { Child };
|
||||||
|
items = [1, 2, 3, 4];
|
||||||
onEv(n, ev) {
|
onEv(n, ev) {
|
||||||
expect(n).toBe(1);
|
expect(n).toBe(1);
|
||||||
expect(ev.detail).toBe(43);
|
expect(ev.detail).toBe(43);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const widget = new ParentWidget(undefined, { items });
|
const widget = new ParentWidget();
|
||||||
await widget.mount(fixture);
|
await widget.mount(fixture);
|
||||||
children(widget)[0].trigger("ev", 43);
|
children(widget)[0].trigger("ev", 43);
|
||||||
expect(env.qweb.templates.ParentWidget.fn.toString()).toMatchSnapshot();
|
expect(env.qweb.templates[ParentWidget.template].fn.toString()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("updating widget immediately", async () => {
|
test("updating widget immediately", async () => {
|
||||||
|
|||||||
@@ -37,17 +37,32 @@ describe("props validation", () => {
|
|||||||
static props = ["message"];
|
static props = ["message"];
|
||||||
static template = xml`<div>hey</div>`;
|
static template = xml`<div>hey</div>`;
|
||||||
}
|
}
|
||||||
|
class Parent extends Widget {
|
||||||
|
static components = { TestWidget };
|
||||||
|
static template = xml`<div><TestWidget /></div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
let error;
|
||||||
QWeb.dev = true;
|
QWeb.dev = true;
|
||||||
expect(() => {
|
try {
|
||||||
new TestWidget();
|
const p = new Parent();
|
||||||
}).toThrow();
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
expect(error.message).toBe(`Missing props 'message' (component 'TestWidget')`);
|
||||||
|
|
||||||
|
error = undefined;
|
||||||
|
|
||||||
QWeb.dev = false;
|
QWeb.dev = false;
|
||||||
|
try {
|
||||||
expect(() => {
|
const p = new Parent();
|
||||||
new TestWidget();
|
await p.mount(fixture);
|
||||||
}).not.toThrow();
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
test("props: list of strings", async () => {
|
test("props: list of strings", async () => {
|
||||||
@@ -55,10 +70,20 @@ describe("props validation", () => {
|
|||||||
static props = ["message"];
|
static props = ["message"];
|
||||||
static template = xml`<div>hey</div>`;
|
static template = xml`<div>hey</div>`;
|
||||||
}
|
}
|
||||||
|
class Parent extends Widget {
|
||||||
|
static components = { TestWidget };
|
||||||
|
static template = xml`<div><TestWidget /></div>`;
|
||||||
|
}
|
||||||
|
|
||||||
expect(() => {
|
let error;
|
||||||
new TestWidget();
|
try {
|
||||||
}).toThrow("Missing props 'message' (component 'TestWidget')");
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
expect(error.message).toBe(`Missing props 'message' (component 'TestWidget')`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("validate simple types", async () => {
|
test("validate simple types", async () => {
|
||||||
@@ -71,23 +96,50 @@ describe("props validation", () => {
|
|||||||
{ type: Function, ok: () => {}, ko: "1" }
|
{ type: Function, ok: () => {}, ko: "1" }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let props;
|
||||||
|
class Parent extends Component<any, any> {
|
||||||
|
static template = xml`<div><TestWidget p="p"/></div>`;
|
||||||
|
get p() {
|
||||||
|
return props.p;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (let test of Tests) {
|
for (let test of Tests) {
|
||||||
let TestWidget = class extends Widget {
|
let TestWidget = class extends Widget {
|
||||||
static template = xml`<div>hey</div>`;
|
static template = xml`<div>hey</div>`;
|
||||||
static props = { p: test.type };
|
static props = { p: test.type };
|
||||||
};
|
};
|
||||||
|
Parent.components = { TestWidget };
|
||||||
|
|
||||||
expect(() => {
|
let error;
|
||||||
new TestWidget();
|
props = {};
|
||||||
}).toThrow("Missing props 'p'");
|
try {
|
||||||
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
expect(error.message).toBe(`Missing props 'p' (component '_a')`);
|
||||||
|
|
||||||
expect(() => {
|
error = undefined;
|
||||||
new TestWidget(undefined, { p: test.ok });
|
props = {p: test.ok};
|
||||||
}).not.toThrow();
|
try {
|
||||||
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
|
||||||
expect(() => {
|
props = {p: test.ko};
|
||||||
new TestWidget(undefined, { p: test.ko });
|
try {
|
||||||
}).toThrow("Props 'p' of invalid type in component");
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
expect(error.message).toBe(`Props 'p' of invalid type in component '_a'`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -101,119 +153,305 @@ describe("props validation", () => {
|
|||||||
{ type: Function, ok: () => {}, ko: "1" }
|
{ type: Function, ok: () => {}, ko: "1" }
|
||||||
];
|
];
|
||||||
|
|
||||||
|
let props;
|
||||||
|
class Parent extends Component<any, any> {
|
||||||
|
static template = xml`<div><TestWidget p="p"/></div>`;
|
||||||
|
get p() {
|
||||||
|
return props.p;
|
||||||
|
}
|
||||||
|
}
|
||||||
for (let test of Tests) {
|
for (let test of Tests) {
|
||||||
let TestWidget = class extends Widget {
|
let TestWidget = class extends Component<any, any> {
|
||||||
static template = xml`<div>hey</div>`;
|
|
||||||
static props = { p: { type: test.type } };
|
static props = { p: { type: test.type } };
|
||||||
|
static template = xml`<div>hey</div>`;
|
||||||
};
|
};
|
||||||
|
Parent.components = { TestWidget };
|
||||||
|
|
||||||
expect(() => {
|
let error;
|
||||||
new TestWidget();
|
props = {};
|
||||||
}).toThrow("Missing props 'p'");
|
try {
|
||||||
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
expect(error.message).toBe(`Missing props 'p' (component '_a')`);
|
||||||
|
|
||||||
expect(() => {
|
error = undefined;
|
||||||
new TestWidget(undefined, { p: test.ok });
|
props = {p: test.ok};
|
||||||
}).not.toThrow();
|
try {
|
||||||
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
|
||||||
expect(() => {
|
props = {p: test.ko};
|
||||||
new TestWidget(undefined, { p: test.ko });
|
try {
|
||||||
}).toThrow("Props 'p' of invalid type in component");
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
expect(error.message).toBe(`Props 'p' of invalid type in component '_a'`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can validate a prop with multiple types", async () => {
|
test("can validate a prop with multiple types", async () => {
|
||||||
let TestWidget = class extends Widget {
|
class TestWidget extends Component<any, any> {
|
||||||
static template = xml`<div>hey</div>`;
|
static template = xml`<div>hey</div>`;
|
||||||
static props = { p: [String, Boolean] };
|
static props = { p: [String, Boolean] };
|
||||||
};
|
};
|
||||||
|
class Parent extends Component<any, any> {
|
||||||
|
static template = xml`<div><TestWidget p="p"/></div>`;
|
||||||
|
static components = { TestWidget };
|
||||||
|
get p() {
|
||||||
|
return props.p;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
expect(() => {
|
let error;
|
||||||
new TestWidget(undefined, { p: "string" });
|
let props;
|
||||||
new TestWidget(undefined, { p: true });
|
try {
|
||||||
}).not.toThrow();
|
props = { p: "string" };
|
||||||
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
|
||||||
expect(() => {
|
try {
|
||||||
new TestWidget(undefined, { p: 1 });
|
props = { p: true };
|
||||||
}).toThrow("Props 'p' of invalid type in component");
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
|
||||||
|
try {
|
||||||
|
props = { p: 1 };
|
||||||
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
expect(error.message).toBe(`Props 'p' of invalid type in component 'TestWidget'`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can validate an optional props", async () => {
|
test("can validate an optional props", async () => {
|
||||||
let TestWidget = class extends Widget {
|
class TestWidget extends Component<any, any> {
|
||||||
static template = xml`<div>hey</div>`;
|
static template = xml`<div>hey</div>`;
|
||||||
static props = { p: { type: String, optional: true } };
|
static props = { p: { type: String, optional: true } };
|
||||||
};
|
};
|
||||||
|
class Parent extends Component<any, any> {
|
||||||
|
static template = xml`<div><TestWidget p="p"/></div>`;
|
||||||
|
static components = { TestWidget };
|
||||||
|
get p() {
|
||||||
|
return props.p;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
expect(() => {
|
let error;
|
||||||
new TestWidget(undefined, { p: "hey" });
|
let props;
|
||||||
new TestWidget(undefined, {});
|
try {
|
||||||
}).not.toThrow();
|
props = { p: "key" };
|
||||||
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
|
||||||
expect(() => {
|
try {
|
||||||
new TestWidget(undefined, { p: 1 });
|
props = {};
|
||||||
}).toThrow();
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
|
||||||
|
try {
|
||||||
|
props = { p: 1 };
|
||||||
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
expect(error.message).toBe(`Props 'p' of invalid type in component 'TestWidget'`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can validate an array with given primitive type", async () => {
|
test("can validate an array with given primitive type", async () => {
|
||||||
let TestWidget = class extends Widget {
|
class TestWidget extends Component<any, any> {
|
||||||
static template = xml`<div>hey</div>`;
|
static template = xml`<div>hey</div>`;
|
||||||
static props = { p: { type: Array, element: String } };
|
static props = { p: { type: Array, element: String } };
|
||||||
};
|
};
|
||||||
|
class Parent extends Component<any, any> {
|
||||||
|
static template = xml`<div><TestWidget p="p"/></div>`;
|
||||||
|
static components = { TestWidget };
|
||||||
|
get p() {
|
||||||
|
return props.p;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
expect(() => {
|
let error;
|
||||||
new TestWidget(undefined, { p: [] });
|
let props;
|
||||||
new TestWidget(undefined, { p: ["string"] });
|
try {
|
||||||
}).not.toThrow();
|
props = { p: [] };
|
||||||
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
|
||||||
expect(() => {
|
try {
|
||||||
new TestWidget(undefined, { p: [1] });
|
props = { p: ["string"] };
|
||||||
}).toThrow();
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
|
||||||
expect(() => {
|
try {
|
||||||
new TestWidget(undefined, { p: ["string", 1] });
|
props = { p: [1] };
|
||||||
}).toThrow();
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
|
||||||
|
error = undefined;
|
||||||
|
try {
|
||||||
|
props = { p: ["string", 1] };
|
||||||
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can validate an array with multiple sub element types", async () => {
|
test("can validate an array with multiple sub element types", async () => {
|
||||||
let TestWidget = class extends Widget {
|
class TestWidget extends Component<any, any> {
|
||||||
static template = xml`<div>hey</div>`;
|
static template = xml`<div>hey</div>`;
|
||||||
static props = { p: { type: Array, element: [String, Boolean] } };
|
static props = { p: { type: Array, element: [String, Boolean] } };
|
||||||
};
|
};
|
||||||
|
class Parent extends Component<any, any> {
|
||||||
|
static template = xml`<div><TestWidget p="p"/></div>`;
|
||||||
|
static components = { TestWidget };
|
||||||
|
get p() {
|
||||||
|
return props.p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
expect(() => {
|
let error;
|
||||||
new TestWidget(undefined, { p: [] });
|
let props;
|
||||||
new TestWidget(undefined, { p: ["string"] });
|
try {
|
||||||
new TestWidget(undefined, { p: [false, true, "string"] });
|
props = { p: [] };
|
||||||
}).not.toThrow();
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
|
||||||
expect(() => {
|
try {
|
||||||
new TestWidget(undefined, { p: [true, 1] });
|
props = { p: ["string"] };
|
||||||
}).toThrow();
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
|
||||||
|
try {
|
||||||
|
props = { p: [false, true, "string"] };
|
||||||
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
|
||||||
|
try {
|
||||||
|
props = { p: [true, 1] };
|
||||||
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
expect(error.message).toBe(`Props 'p' of invalid type in component 'TestWidget'`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can validate an object with simple shape", async () => {
|
test("can validate an object with simple shape", async () => {
|
||||||
let TestWidget = class extends Widget {
|
class TestWidget extends Component<any, any> {
|
||||||
static template = xml`<div>hey</div>`;
|
static template = xml`<div>hey</div>`;
|
||||||
static props = {
|
static props = {
|
||||||
p: { type: Object, shape: { id: Number, url: String } }
|
p: { type: Object, shape: { id: Number, url: String } }
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
class Parent extends Component<any, any> {
|
||||||
|
static template = xml`<div><TestWidget p="p"/></div>`;
|
||||||
|
static components = { TestWidget };
|
||||||
|
get p() {
|
||||||
|
return props.p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
expect(() => {
|
let error;
|
||||||
new TestWidget(undefined, { p: { id: 1, url: "url" } });
|
let props;
|
||||||
new TestWidget(undefined, { p: { id: 1, url: "url", extra: true } });
|
try {
|
||||||
}).not.toThrow();
|
props = { p: { id: 1, url: "url" } };
|
||||||
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
|
||||||
expect(() => {
|
try {
|
||||||
new TestWidget(undefined, { p: { id: "1", url: "url" } });
|
props = { p: { id: 1, url: "url", extra: true } };
|
||||||
}).toThrow();
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
|
||||||
expect(() => {
|
try {
|
||||||
new TestWidget(undefined, { p: { id: 1 } });
|
props = { p: { id: "1", url: "url" } };
|
||||||
}).toThrow();
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
expect(error.message).toBe(`Props 'p' of invalid type in component 'TestWidget'`);
|
||||||
|
|
||||||
|
error = undefined;
|
||||||
|
try {
|
||||||
|
props = { p: { id: 1 } };
|
||||||
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
expect(error.message).toBe(`Props 'p' of invalid type in component 'TestWidget'`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can validate recursively complicated prop def", async () => {
|
test("can validate recursively complicated prop def", async () => {
|
||||||
let TestWidget = class extends Widget {
|
class TestWidget extends Component<any, any> {
|
||||||
static template = xml`<div>hey</div>`;
|
static template = xml`<div>hey</div>`;
|
||||||
static props = {
|
static props = {
|
||||||
p: {
|
p: {
|
||||||
@@ -225,15 +463,43 @@ describe("props validation", () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
class Parent extends Component<any, any> {
|
||||||
|
static template = xml`<div><TestWidget p="p"/></div>`;
|
||||||
|
static components = { TestWidget };
|
||||||
|
get p() {
|
||||||
|
return props.p;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
expect(() => {
|
let error;
|
||||||
new TestWidget(undefined, { p: { id: 1, url: true } });
|
let props;
|
||||||
new TestWidget(undefined, { p: { id: 1, url: [12] } });
|
try {
|
||||||
}).not.toThrow();
|
props = { p: { id: 1, url: true } };
|
||||||
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
|
||||||
expect(() => {
|
try {
|
||||||
new TestWidget(undefined, { p: { id: 1, url: [12, true] } });
|
props = { p: { id: 1, url: [12] } };
|
||||||
}).toThrow();
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeUndefined();
|
||||||
|
|
||||||
|
try {
|
||||||
|
props = { p: { id: 1, url: [12, true] } };
|
||||||
|
const p = new Parent();
|
||||||
|
await p.mount(fixture);
|
||||||
|
} catch (e) {
|
||||||
|
error = e;
|
||||||
|
}
|
||||||
|
expect(error).toBeDefined();
|
||||||
|
expect(error.message).toBe(`Props 'p' of invalid type in component 'TestWidget'`);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("props are validated in dev mode (code snapshot)", async () => {
|
test("props are validated in dev mode (code snapshot)", async () => {
|
||||||
@@ -401,13 +667,18 @@ describe("props validation", () => {
|
|||||||
|
|
||||||
describe("default props", () => {
|
describe("default props", () => {
|
||||||
test("can set default values", async () => {
|
test("can set default values", async () => {
|
||||||
class TestWidget extends Widget {
|
class TestWidget extends Component<any, any> {
|
||||||
static defaultProps = { p: 4 };
|
static defaultProps = { p: 4 };
|
||||||
static template = xml`<div>hey</div>`;
|
static template = xml`<div><t t-esc="props.p"/></div>`;
|
||||||
|
}
|
||||||
|
class Parent extends Component<any, any> {
|
||||||
|
static template = xml`<div><TestWidget /></div>`;
|
||||||
|
static components = { TestWidget };
|
||||||
}
|
}
|
||||||
|
|
||||||
const w = new TestWidget(undefined, {});
|
const w = new Parent();
|
||||||
expect(w.props.p).toBe(4);
|
await w.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe('<div><div>4</div></div>');
|
||||||
});
|
});
|
||||||
|
|
||||||
test("default values are also set whenever component is updated", async () => {
|
test("default values are also set whenever component is updated", async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user