mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[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:
committed by
Aaron Bohy
parent
15e4c856da
commit
c16d8ed6de
+24
-8
@@ -5,13 +5,24 @@ import { TemplateSet } from "./qweb/template_helpers";
|
||||
|
||||
// reimplement dev mode stuff see last change in 0f7a8289a6fb8387c3c1af41c6664b2a8448758f
|
||||
|
||||
interface Config {
|
||||
dev?: boolean;
|
||||
env?: { [key: string]: any };
|
||||
translatableAttributes?: string[];
|
||||
translateFn?: (s: string) => string;
|
||||
}
|
||||
|
||||
export const DEV_MSG = `Owl is running in 'dev' mode.
|
||||
|
||||
This is not suitable for production use.
|
||||
See https://github.com/odoo/owl/blob/master/doc/reference/config.md#mode for more information.`;
|
||||
|
||||
export class App<T extends typeof Component = any> extends TemplateSet {
|
||||
Root: T;
|
||||
props: any;
|
||||
env: any = {};
|
||||
scheduler = new Scheduler(window.requestAnimationFrame.bind(window));
|
||||
root: ComponentNode | null = null;
|
||||
dev: boolean = true;
|
||||
|
||||
constructor(Root: T, props?: any) {
|
||||
super();
|
||||
@@ -19,15 +30,20 @@ export class App<T extends typeof Component = any> extends TemplateSet {
|
||||
this.props = props;
|
||||
}
|
||||
|
||||
configure(params: any) {
|
||||
if (params.env) {
|
||||
this.env = params.env;
|
||||
configure(config: Config) {
|
||||
if (config.dev) {
|
||||
this.dev = config.dev;
|
||||
console.info(DEV_MSG);
|
||||
}
|
||||
if (params.translateFn) {
|
||||
this.translateFn = params.translateFn;
|
||||
|
||||
if (config.env) {
|
||||
this.env = config.env;
|
||||
}
|
||||
if (params.translatableAttributes) {
|
||||
this.translatableAttributes = params.translatableAttributes;
|
||||
if (config.translateFn) {
|
||||
this.translateFn = config.translateFn;
|
||||
}
|
||||
if (config.translatableAttributes) {
|
||||
this.translatableAttributes = config.translatableAttributes;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,120 @@
|
||||
import { Component } from "./component";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Prop validation helper
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Validate the component props (or next props) against the (static) props
|
||||
* description. This is potentially an expensive operation: it may needs to
|
||||
* visit recursively the props and all the children to check if they are valid.
|
||||
* This is why it is only done in 'dev' mode.
|
||||
*/
|
||||
|
||||
// message: { type: String, default: "hello" };
|
||||
|
||||
export const validateProps = function (name: string | typeof Component, props: any, parent?: any) {
|
||||
const ComponentClass =
|
||||
typeof name !== "string" ? name : parent.constructor.components[name as string];
|
||||
|
||||
const propsDef = (<any>ComponentClass).props;
|
||||
if (propsDef instanceof Array) {
|
||||
// list of strings (prop names)
|
||||
for (let i = 0, l = propsDef.length; i < l; i++) {
|
||||
const propName = propsDef[i];
|
||||
if (propName[propName.length - 1] === "?") {
|
||||
// optional prop
|
||||
break;
|
||||
}
|
||||
if (!(propName in props)) {
|
||||
throw new Error(`Missing props '${propsDef[i]}' (component '${ComponentClass.name}')`);
|
||||
}
|
||||
}
|
||||
for (let key in props) {
|
||||
if (!propsDef.includes(key) && !propsDef.includes(key + "?")) {
|
||||
throw new Error(`Unknown prop '${key}' given to component '${ComponentClass.name}'`);
|
||||
}
|
||||
}
|
||||
} else if (propsDef) {
|
||||
// propsDef is an object now
|
||||
for (let propName in propsDef) {
|
||||
if (props[propName] === undefined) {
|
||||
if (propsDef[propName] && !propsDef[propName].optional) {
|
||||
throw new Error(`Missing props '${propName}' (component '${ComponentClass.name}')`);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let isValid;
|
||||
try {
|
||||
isValid = isValidProp(props[propName], propsDef[propName]);
|
||||
} catch (e) {
|
||||
e.message = `Invalid prop '${propName}' in component ${ComponentClass.name} (${e.message})`;
|
||||
throw e;
|
||||
}
|
||||
if (!isValid) {
|
||||
throw new Error(`Invalid Prop '${propName}' in component '${ComponentClass.name}'`);
|
||||
}
|
||||
}
|
||||
for (let propName in props) {
|
||||
if (!(propName in propsDef)) {
|
||||
throw new Error(`Unknown prop '${propName}' given to component '${ComponentClass.name}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if an invidual prop value matches its (static) prop definition
|
||||
*/
|
||||
function isValidProp(prop: any, propDef: any): boolean {
|
||||
if (propDef === true) {
|
||||
return true;
|
||||
}
|
||||
if (typeof propDef === "function") {
|
||||
// Check if a value is constructed by some Constructor. Note that there is a
|
||||
// slight abuse of language: we want to consider primitive values as well.
|
||||
//
|
||||
// So, even though 1 is not an instance of Number, we want to consider that
|
||||
// it is valid.
|
||||
if (typeof prop === "object") {
|
||||
return prop instanceof propDef;
|
||||
}
|
||||
return typeof prop === propDef.name.toLowerCase();
|
||||
} else if (propDef instanceof Array) {
|
||||
// If this code is executed, this means that we want to check if a prop
|
||||
// matches at least one of its descriptor.
|
||||
let result = false;
|
||||
for (let i = 0, iLen = propDef.length; i < iLen; i++) {
|
||||
result = result || isValidProp(prop, propDef[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// propsDef is an object
|
||||
if (propDef.optional && prop === undefined) {
|
||||
return true;
|
||||
}
|
||||
let result = propDef.type ? isValidProp(prop, propDef.type) : true;
|
||||
if (propDef.validate) {
|
||||
result = result && propDef.validate(prop);
|
||||
}
|
||||
if (propDef.type === Array && propDef.element) {
|
||||
for (let i = 0, iLen = prop.length; i < iLen; i++) {
|
||||
result = result && isValidProp(prop[i], propDef.element);
|
||||
}
|
||||
}
|
||||
if (propDef.type === Object && propDef.shape) {
|
||||
const shape = propDef.shape;
|
||||
for (let key in shape) {
|
||||
result = result && isValidProp(prop[key], shape[key]);
|
||||
}
|
||||
if (result) {
|
||||
for (let propName in prop) {
|
||||
if (!(propName in shape)) {
|
||||
throw new Error(`unknown prop '${propName}'`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -32,6 +32,7 @@ export interface CompileOptions {
|
||||
name?: string;
|
||||
translateFn?: (s: string) => string;
|
||||
translatableAttributes?: string[];
|
||||
dev?: boolean;
|
||||
}
|
||||
|
||||
export function compileTemplate(template: string, options?: CompileOptions): TemplateFunction {
|
||||
@@ -177,6 +178,7 @@ export class QWebCompiler {
|
||||
target = new CodeTarget("main");
|
||||
templateName: string;
|
||||
template: string;
|
||||
dev: boolean;
|
||||
translateFn: (s: string) => string;
|
||||
translatableAttributes: string[];
|
||||
ast: AST;
|
||||
@@ -186,6 +188,7 @@ export class QWebCompiler {
|
||||
this.template = template;
|
||||
this.translateFn = options.translateFn || ((s: string) => s);
|
||||
this.translatableAttributes = options.translatableAttributes || TRANSLATABLE_ATTRS;
|
||||
this.dev = options.dev || false;
|
||||
this.ast = parse(template);
|
||||
if (options.name) {
|
||||
this.templateName = options.name;
|
||||
@@ -925,6 +928,14 @@ export class QWebCompiler {
|
||||
} else {
|
||||
expr = `\`${ast.name}\``;
|
||||
}
|
||||
|
||||
if (this.dev) {
|
||||
const propVar = this.generateId("props");
|
||||
this.addLine(`const ${propVar} = ${propString}`);
|
||||
this.addLine(`helpers.validateProps(${expr}, ${propVar}, ctx)`);
|
||||
propString = propVar;
|
||||
}
|
||||
|
||||
let blockArgs = `${expr}, ${propString}, key + \`${key}\`, node, ctx`;
|
||||
|
||||
// slots
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { BDom, createBlock, html, list, multi, text, toggler } from "../blockdom";
|
||||
import { compileTemplate, Template } from "./compiler";
|
||||
import { component } from "../component/component_node";
|
||||
import { validateProps } from "../component/props_validation";
|
||||
|
||||
const bdom = { text, createBlock, list, multi, html, toggler, component };
|
||||
|
||||
@@ -94,6 +95,7 @@ export const UTILS = {
|
||||
prepareList,
|
||||
setContextValue,
|
||||
shallowEqual,
|
||||
validateProps,
|
||||
};
|
||||
|
||||
export class TemplateSet {
|
||||
@@ -102,6 +104,7 @@ export class TemplateSet {
|
||||
translateFn?: (s: string) => string;
|
||||
translatableAttributes?: string[];
|
||||
utils: typeof UTILS;
|
||||
dev?: boolean;
|
||||
|
||||
constructor() {
|
||||
const call = (subTemplate: string, ctx: any, parent: any) => {
|
||||
@@ -128,6 +131,7 @@ export class TemplateSet {
|
||||
}
|
||||
const templateFn = compileTemplate(rawTemplate, {
|
||||
name,
|
||||
dev: this.dev,
|
||||
translateFn: this.translateFn,
|
||||
translatableAttributes: this.translatableAttributes,
|
||||
});
|
||||
|
||||
@@ -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]);
|
||||
}
|
||||
}"
|
||||
`;
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
@@ -320,6 +320,6 @@ describe("event handling", () => {
|
||||
}
|
||||
|
||||
await mount(Parent, fixture);
|
||||
(<HTMLElement>fixture.querySelector('.item')).click();
|
||||
(<HTMLElement>fixture.querySelector(".item")).click();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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", () => {
|
||||
|
||||
@@ -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
@@ -46,6 +46,7 @@ export function snapshotApp(app: App) {
|
||||
snapshotTemplateCode(template, {
|
||||
translateFn: app.translateFn,
|
||||
translatableAttributes: app.translatableAttributes,
|
||||
dev: app.dev,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
|
||||
|
||||
@@ -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'"
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user