[FIX] component: updateProps: validation and default values

Before this rev., default values weren't taken into account when
validating props whenever a component was updated. Moreover, there
was no test attesting that props were validated at update.
This commit is contained in:
Aaron Bohy
2019-10-28 11:14:29 +01:00
parent 4ebe419c56
commit 5d57a7bf13
4 changed files with 55 additions and 5 deletions
+3
View File
@@ -503,6 +503,9 @@ export class Component<T extends Env, Props extends {}> {
if (defaultProps) { if (defaultProps) {
nextProps = this.__applyDefaultProps(nextProps, defaultProps); nextProps = this.__applyDefaultProps(nextProps, defaultProps);
} }
if (QWeb.dev) {
QWeb.utils.validateProps(this.constructor, nextProps);
}
await Promise.all([ await Promise.all([
this.willUpdateProps(nextProps), this.willUpdateProps(nextProps),
__owl__.willUpdatePropsCB && __owl__.willUpdatePropsCB(nextProps) __owl__.willUpdatePropsCB && __owl__.willUpdatePropsCB(nextProps)
-3
View File
@@ -412,9 +412,6 @@ QWeb.addDirective({
// disable the patch queue // disable the patch queue
patchQueueCode = `w${componentID}.__owl__.isMounted ? extra.fiber : fiber${componentID}`; patchQueueCode = `w${componentID}.__owl__.isMounted ? extra.fiber : fiber${componentID}`;
} }
if (QWeb.dev) {
ctx.addLine(`utils.validateProps(w${componentID}.constructor, props${componentID})`);
}
let styleCode = ""; let styleCode = "";
if (tattStyle) { if (tattStyle) {
styleCode = `.then(()=>{if (w${componentID}.__owl__.isDestroyed) {return};w${componentID}.el.style=${tattStyle};});`; styleCode = `.then(()=>{if (w${componentID}.__owl__.isDestroyed) {return};w${componentID}.el.style=${tattStyle};});`;
@@ -20,7 +20,6 @@ exports[`props validation props are validated in dev mode (code snapshot) 1`] =
w3 = false; w3 = false;
} }
if (w3) { if (w3) {
utils.validateProps(w3.constructor, props3)
w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling); w3.__updateProps(props3, extra.fiber, undefined, undefined, sibling);
let pvnode = w3.__owl__.pvnode; let pvnode = w3.__owl__.pvnode;
c1.push(pvnode); c1.push(pvnode);
+52 -1
View File
@@ -323,7 +323,7 @@ describe("props validation", () => {
}).toThrow(); }).toThrow();
}); });
test("can set default required boolean values", async () => { test("missing required boolean prop causes an error", async () => {
class TestWidget extends Widget { class TestWidget extends Widget {
static props = ["p"]; static props = ["p"];
static template = xml`<span><t t-if="props.p">hey</t></span>`; static template = xml`<span><t t-if="props.p">hey</t></span>`;
@@ -344,6 +344,57 @@ describe("props validation", () => {
expect(error).toBeDefined(); expect(error).toBeDefined();
expect(error.message).toBe("Missing props 'p' (component 'TestWidget')"); expect(error.message).toBe("Missing props 'p' (component 'TestWidget')");
}); });
test("props are validated whenever component is updated", async () => {
let error;
class TestWidget extends Component<any, any> {
static props = { p: { type: Number } };
static template = xml`<div><t t-esc="props.p"/></div>`;
async __updateProps() {
try {
await Component.prototype.__updateProps.apply(this, arguments);
} catch(e) {
error = e;
}
}
}
class Parent extends Component<any, any> {
static template = xml`<div><TestWidget p="state.p"/></div>`;
static components = { TestWidget };
state: any = useState({ p: 1 });
}
const w = new Parent(env);
await w.mount(fixture);
expect(fixture.innerHTML).toBe("<div><div>1</div></div>");
w.state.p = undefined;
await nextTick();
expect(error).toBeDefined();
expect(error.message).toBe("Missing props 'p' (component 'TestWidget')");
});
test("default values are applied before validating props at update", async () => {
class TestWidget extends Component<any, any> {
static props = { p: { type: Number } };
static template = xml`<div><t t-esc="props.p"/></div>`;
static defaultProps = { p: 4 };
}
class Parent extends Component<any, any> {
static template = xml`<div><TestWidget p="state.p"/></div>`;
static components = { TestWidget };
state: any = useState({ p: 1 });
}
const w = new Parent(env);
await w.mount(fixture);
expect(fixture.innerHTML).toBe("<div><div>1</div></div>");
w.state.p = undefined;
await nextTick();
expect(fixture.innerHTML).toBe("<div><div>4</div></div>");
});
}); });
describe("default props", () => { describe("default props", () => {