[IMP] component,qweb: props validation

We re-add the possibility to validate props when dev mode is active.
No change in the API right Now. The dev mode is activated via the
configure method of App class.
This commit is contained in:
Mathieu Duckerts-Antoine
2021-10-20 16:08:31 +02:00
committed by Aaron Bohy
parent 15e4c856da
commit c16d8ed6de
15 changed files with 1308 additions and 714 deletions
@@ -0,0 +1,458 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`props validation can validate a prop with multiple types 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation can validate a prop with multiple types 2`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation can validate an array with given primitive type 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation can validate an array with given primitive type 2`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation can validate an array with multiple sub element types 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation can validate an array with multiple sub element types 2`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation can validate an array with multiple sub element types 3`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation can validate an object with simple shape 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation can validate an optional props 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation can validate an optional props 2`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation can validate recursively complicated prop def 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation can validate recursively complicated prop def 2`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation props are validated in dev mode (code snapshot) 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {message: 1}
helpers.validateProps(\`Child\`, props2, ctx)
let b2 = component(\`Child\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation props are validated whenever component is updated 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['state'].p}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation validate simple types 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation validate simple types 2`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation validate simple types 3`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation validate simple types 4`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation validate simple types 5`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation validate simple types 6`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation validate simple types, alternate form 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation validate simple types, alternate form 2`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation validate simple types, alternate form 3`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation validate simple types, alternate form 4`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation validate simple types, alternate form 5`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation validate simple types, alternate form 6`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
const props2 = {p: ctx['p']}
helpers.validateProps(\`SubComp\`, props2, ctx)
let b2 = component(\`SubComp\`, props2, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
exports[`props validation validation is only done in dev mode 1`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component } = bdom;
let { withDefault, getTemplate, prepareList, withKey, zero, call, callSlot, capture, isBoundary, shallowEqual, setContextValue } = helpers;
let block1 = createBlock(\`<div><block-child-0/></div>\`);
return function template(ctx, node, key = \\"\\") {
let b2 = component(\`SubComp\`, {}, key + \`__1\`, node, ctx);
return block1([], [b2]);
}
}"
`;
+1 -1
View File
@@ -750,7 +750,7 @@ describe("basics", () => {
static template = xml`<span>abc<t t-if="props.flag">def</t></span>`;
}
class Parent extends Component {
static template = xml`<Child flag="state.flag"/>`
static template = xml`<Child flag="state.flag"/>`;
static components = { Child };
state = useState({ flag: false });
}
+1 -1
View File
@@ -320,6 +320,6 @@ describe("event handling", () => {
}
await mount(Parent, fixture);
(<HTMLElement>fixture.querySelector('.item')).click();
(<HTMLElement>fixture.querySelector(".item")).click();
});
});
+21 -6
View File
@@ -1,9 +1,19 @@
import { App, Component, mount, useRef, useState, useComponent, onMounted, onPatched, onWillStart, onWillUpdateProps, onWillPatch, xml, onWillUnmount } from "../../src";
import {
makeTestFixture,
nextTick,
snapshotEverything,
} from "../helpers";
App,
Component,
mount,
useRef,
useState,
useComponent,
onMounted,
onPatched,
onWillStart,
onWillUpdateProps,
onWillPatch,
xml,
onWillUnmount,
} from "../../src";
import { makeTestFixture, nextTick, snapshotEverything } from "../helpers";
let fixture: HTMLElement;
@@ -85,7 +95,12 @@ describe("hooks", () => {
const app = new App(Test);
await app.mount(fixture);
app.destroy();
expect(steps).toEqual(["hook:mounted2", "hook:mounted1", "hook:willUnmount1", "hook:willUnmount2"]);
expect(steps).toEqual([
"hook:mounted2",
"hook:mounted1",
"hook:willUnmount1",
"hook:willUnmount2",
]);
});
describe("autofocus hook", () => {
+2 -2
View File
@@ -1070,7 +1070,7 @@ describe("lifecycle hooks", () => {
class Child extends Component {
static template = xml`<div>child</div>`;
setup() {
useLogLifecycle(steps)
useLogLifecycle(steps);
}
}
@@ -1110,7 +1110,7 @@ describe("lifecycle hooks", () => {
"Child:render",
"Parent:willPatch",
"Child:mounted",
"Parent:patched"
"Parent:patched",
]);
Object.freeze(steps);
});
File diff suppressed because it is too large Load Diff
+1
View File
@@ -46,6 +46,7 @@ export function snapshotApp(app: App) {
snapshotTemplateCode(template, {
translateFn: app.translateFn,
translatableAttributes: app.translatableAttributes,
dev: app.dev,
});
}
+3 -3
View File
@@ -33,7 +33,7 @@ describe("Asyncroot", () => {
</AsyncRoot>
</div>
</div>`;
static components = { Child, AsyncChild, /*AsyncRoot*/ };
static components = { Child, AsyncChild /*AsyncRoot*/ };
state = useState({ val: 0 });
updateApp() {
@@ -79,7 +79,7 @@ describe("Asyncroot", () => {
<AsyncChild val="state.val"/>
</div>
</div>`;
static components = { Child, AsyncChild, /*AsyncRoot*/ };
static components = { Child, AsyncChild /*AsyncRoot*/ };
state = useState({ val: 0 });
updateApp() {
@@ -132,7 +132,7 @@ describe("Asyncroot", () => {
</AsyncRoot>
</div>
</div>`;
static components = { Child, AsyncChild, /*AsyncRoot*/ };
static components = { Child, AsyncChild /*AsyncRoot*/ };
state = useState({ val: 0 });
updateApp() {
+2 -1
View File
@@ -405,7 +405,8 @@ describe("Portal", () => {
error = e;
}
expect(error).toBeDefined();
const regexp = /Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g
const regexp =
/Cannot read properties of undefined \(reading 'crash'\)|Cannot read property 'crash' of undefined/g;
expect(error.message).toMatch(regexp);
});
+4 -2
View File
@@ -15,7 +15,9 @@ describe("error handling", () => {
test("cannot add twice the same template", () => {
const context = new TestContext();
context.addTemplate("test", `<t></t>`);
expect(() => context.addTemplate("test", "<div/>", { allowDuplicate: true })).not.toThrow("already defined");
expect(() => context.addTemplate("test", "<div/>", { allowDuplicate: true })).not.toThrow(
"already defined"
);
expect(() => context.addTemplate("test", "<div/>")).toThrow("already defined");
});
@@ -37,4 +39,4 @@ describe("error handling", () => {
"Unknown QWeb directive: 't-best-beer'"
);
});
});
});
+2 -5
View File
@@ -1,7 +1,4 @@
import {
renderToString,
snapshotEverything,
} from "../helpers";
import { renderToString, snapshotEverything } from "../helpers";
snapshotEverything();
@@ -28,4 +25,4 @@ describe("qweb t-tag", () => {
const expected = `<div taste=\"raspberry\" class=\"blueberry\">gooseberry</div>`;
expect(renderToString(template, { tag: "div" })).toBe(expected);
});
});
});