[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 Géry Debongnie
parent d1425c7100
commit 6e0834ce5e
15 changed files with 1308 additions and 714 deletions
+24 -8
View File
@@ -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;
}
}
+120
View File
@@ -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;
}
+11
View File
@@ -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
+4
View File
@@ -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]);
}
}"
`;
+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);
});
});
});