mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
Compare commits
5 Commits
v1.4.9
..
owl-1.4.10
| Author | SHA1 | Date | |
|---|---|---|---|
| 0d0f64c0ed | |||
| 940ac64340 | |||
| c06049076a | |||
| bc04f727ac | |||
| 0bc9573a8a |
@@ -124,7 +124,7 @@ npm install @odoo/owl
|
|||||||
|
|
||||||
If you want to use a simple `<script>` tag, the last release can be downloaded here:
|
If you want to use a simple `<script>` tag, the last release can be downloaded here:
|
||||||
|
|
||||||
- [owl-1.4.9](https://github.com/odoo/owl/releases/tag/v1.4.9)
|
- [owl-1.4.10](https://github.com/odoo/owl/releases/tag/v1.4.10)
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@odoo/owl",
|
"name": "@odoo/owl",
|
||||||
"version": "1.4.9",
|
"version": "1.4.10",
|
||||||
"description": "Odoo Web Library (OWL)",
|
"description": "Odoo Web Library (OWL)",
|
||||||
"main": "dist/owl.cjs.js",
|
"main": "dist/owl.cjs.js",
|
||||||
"browser": "dist/owl.iife.js",
|
"browser": "dist/owl.iife.js",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# 🦉 OWL Roadmap 🦉
|
# 🦉 OWL Roadmap 🦉
|
||||||
|
|
||||||
- Current version: 1.4.9
|
- Current version: 1.4.10
|
||||||
- Status: stable
|
- Status: stable
|
||||||
|
|
||||||
This roadmap is only an attempt at predicting Owl's future. Everything may
|
This roadmap is only an attempt at predicting Owl's future. Everything may
|
||||||
|
|||||||
@@ -40,15 +40,16 @@ QWeb.utils.validateProps = function (Widget, props: Object) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let isValid;
|
let whyInvalid;
|
||||||
try {
|
try {
|
||||||
isValid = isValidProp(props[propName], propsDef[propName]);
|
whyInvalid = whyInvalidProp(props[propName], propsDef[propName]);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
e.message = `Invalid prop '${propName}' in component ${Widget.name} (${e.message})`;
|
e.message = `Invalid prop '${propName}' in component ${Widget.name} (${e.message})`;
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
if (!isValid) {
|
if (whyInvalid !== null) {
|
||||||
throw new Error(`Invalid Prop '${propName}' in component '${Widget.name}'`);
|
whyInvalid = whyInvalid.replace(/\${propName}/g, propName);
|
||||||
|
throw new Error(`Invalid Prop '${propName}' in component '${Widget.name}': ${whyInvalid}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (let propName in props) {
|
for (let propName in props) {
|
||||||
@@ -60,11 +61,11 @@ QWeb.utils.validateProps = function (Widget, props: Object) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if an invidual prop value matches its (static) prop definition
|
* Check why an invidual prop value doesn't match its (static) prop definition
|
||||||
*/
|
*/
|
||||||
function isValidProp(prop, propDef): boolean {
|
function whyInvalidProp(prop, propDef): string | null {
|
||||||
if (propDef === true) {
|
if (propDef === true) {
|
||||||
return true;
|
return null;
|
||||||
}
|
}
|
||||||
if (typeof propDef === "function") {
|
if (typeof propDef === "function") {
|
||||||
// Check if a value is constructed by some Constructor. Note that there is a
|
// Check if a value is constructed by some Constructor. Note that there is a
|
||||||
@@ -73,43 +74,66 @@ function isValidProp(prop, propDef): boolean {
|
|||||||
// So, even though 1 is not an instance of Number, we want to consider that
|
// So, even though 1 is not an instance of Number, we want to consider that
|
||||||
// it is valid.
|
// it is valid.
|
||||||
if (typeof prop === "object") {
|
if (typeof prop === "object") {
|
||||||
return prop instanceof propDef;
|
if (prop instanceof propDef) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return `\${propName} is not an instance of ${propDef.name}`;
|
||||||
}
|
}
|
||||||
return typeof prop === propDef.name.toLowerCase();
|
if (typeof prop === propDef.name.toLowerCase()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return `type of \${propName} is not ${propDef.name}`;
|
||||||
} else if (propDef instanceof Array) {
|
} else if (propDef instanceof Array) {
|
||||||
// If this code is executed, this means that we want to check if a prop
|
// If this code is executed, this means that we want to check if a prop
|
||||||
// matches at least one of its descriptor.
|
// matches at least one of its descriptor.
|
||||||
let result = false;
|
let reasons: string[] = [];
|
||||||
for (let i = 0, iLen = propDef.length; i < iLen; i++) {
|
for (let i = 0, iLen = propDef.length; i < iLen; i++) {
|
||||||
result = result || isValidProp(prop, propDef[i]);
|
const why = whyInvalidProp(prop, propDef[i]);
|
||||||
|
if (why === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
reasons.push(why);
|
||||||
|
}
|
||||||
|
if (reasons.length > 1) {
|
||||||
|
return reasons.slice(0, -1).join(", ") + " and " + reasons[reasons.length - 1];
|
||||||
|
} else {
|
||||||
|
return reasons[0];
|
||||||
}
|
}
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
// propsDef is an object
|
// propsDef is an object
|
||||||
if (propDef.optional && prop === undefined) {
|
if (propDef.optional && prop === undefined) {
|
||||||
return true;
|
return null;
|
||||||
}
|
}
|
||||||
let result = propDef.type ? isValidProp(prop, propDef.type) : true;
|
if (propDef.type) {
|
||||||
if (propDef.validate) {
|
const why = whyInvalidProp(prop, propDef.type);
|
||||||
result = result && propDef.validate(prop);
|
if (why !== null) {
|
||||||
|
return why;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (propDef.validate && !propDef.validate(prop)) {
|
||||||
|
return "${propName} could not be validated by `validate` function";
|
||||||
}
|
}
|
||||||
if (propDef.type === Array && propDef.element) {
|
if (propDef.type === Array && propDef.element) {
|
||||||
for (let i = 0, iLen = prop.length; i < iLen; i++) {
|
for (let i = 0, iLen = prop.length; i < iLen; i++) {
|
||||||
result = result && isValidProp(prop[i], propDef.element);
|
const why = whyInvalidProp(prop[i], propDef.element);
|
||||||
|
if (why !== null) {
|
||||||
|
return why.replace(/\${propName}/g, `\${propName}[${i}]`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (propDef.type === Object && propDef.shape) {
|
if (propDef.type === Object && propDef.shape) {
|
||||||
const shape = propDef.shape;
|
const shape = propDef.shape;
|
||||||
for (let key in shape) {
|
for (let key in shape) {
|
||||||
result = result && isValidProp(prop[key], shape[key]);
|
const why = whyInvalidProp(prop[key], shape[key]);
|
||||||
|
if (why !== null) {
|
||||||
|
return why.replace(/\${propName}/g, `\${propName}['${key}']`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (result) {
|
for (let propName in prop) {
|
||||||
for (let propName in prop) {
|
if (!(propName in shape)) {
|
||||||
if (!(propName in shape)) {
|
return `unknown prop \${propName}['${propName}']`;
|
||||||
throw new Error(`unknown prop '${propName}'`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-2
@@ -104,10 +104,18 @@ export function useRef<C extends Component = Component>(name: string): Ref<C> {
|
|||||||
return {
|
return {
|
||||||
get el(): HTMLElement | null {
|
get el(): HTMLElement | null {
|
||||||
const val = __owl__.refs && __owl__.refs[name];
|
const val = __owl__.refs && __owl__.refs[name];
|
||||||
|
if (val instanceof Component) {
|
||||||
|
return val.el;
|
||||||
|
}
|
||||||
if (val instanceof HTMLElement) {
|
if (val instanceof HTMLElement) {
|
||||||
return val;
|
return val;
|
||||||
} else if (val instanceof Component) {
|
}
|
||||||
return val.el;
|
// Extra check in case the app was created outside an iframe but mounted into one
|
||||||
|
// on Firefox 109+, the prototype of the element changes to use the iframe window's HTMLElement
|
||||||
|
// see https://bugzilla.mozilla.org/show_bug.cgi?id=1813499
|
||||||
|
const ownerWindow = (val as any)?.ownerDocument?.defaultView;
|
||||||
|
if (ownerWindow && (val as any) instanceof ownerWindow.HTMLElement) {
|
||||||
|
return val;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -162,7 +162,7 @@ export class CompilationContext {
|
|||||||
const tokens = compileExprToArray(expr, this.variables);
|
const tokens = compileExprToArray(expr, this.variables);
|
||||||
const done = new Set();
|
const done = new Set();
|
||||||
return tokens
|
return tokens
|
||||||
.map((tok) => {
|
.map((tok, i) => {
|
||||||
// "this" in captured expressions should be the current component
|
// "this" in captured expressions should be the current component
|
||||||
if (tok.value === "this") {
|
if (tok.value === "this") {
|
||||||
if (!done.has("this")) {
|
if (!done.has("this")) {
|
||||||
@@ -174,7 +174,14 @@ export class CompilationContext {
|
|||||||
// Variables that should be looked up in the scope. isLocal is for arrow
|
// Variables that should be looked up in the scope. isLocal is for arrow
|
||||||
// function arguments that should stay untouched (eg "ev => ev" should
|
// function arguments that should stay untouched (eg "ev => ev" should
|
||||||
// not become "const ev_1 = scope['ev']; ev_1 => ev_1")
|
// not become "const ev_1 = scope['ev']; ev_1 => ev_1")
|
||||||
if (tok.varName && !tok.isLocal) {
|
if (
|
||||||
|
tok.varName &&
|
||||||
|
!tok.isLocal &&
|
||||||
|
// HACK: for backwards compatibility, we don't capture bare methods
|
||||||
|
// this allows them to be called with the rendering context/scope
|
||||||
|
// as their this value.
|
||||||
|
(!tokens[i + 1] || tokens[i + 1].type !== "LEFT_PAREN")
|
||||||
|
) {
|
||||||
if (!done.has(tok.varName)) {
|
if (!done.has(tok.varName)) {
|
||||||
done.add(tok.varName);
|
done.add(tok.varName);
|
||||||
this.addLine(`const ${tok.varName}_${argId} = ${tok.value};`);
|
this.addLine(`const ${tok.varName}_${argId} = ${tok.value};`);
|
||||||
|
|||||||
+3
-12
@@ -1,7 +1,7 @@
|
|||||||
import { EventBus } from "../core/event_bus";
|
import { EventBus } from "../core/event_bus";
|
||||||
import { h, patch, VNode } from "../vdom/index";
|
import { h, patch, VNode } from "../vdom/index";
|
||||||
import { CompilationContext } from "./compilation_context";
|
import { CompilationContext } from "./compilation_context";
|
||||||
import { shallowEqual, escape } from "../utils";
|
import { shallowEqual } from "../utils";
|
||||||
import { addNS } from "../vdom/vdom";
|
import { addNS } from "../vdom/vdom";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -425,17 +425,8 @@ export class QWeb extends EventBus {
|
|||||||
return vnode.text!;
|
return vnode.text!;
|
||||||
}
|
}
|
||||||
const node = document.createElement(vnode.sel);
|
const node = document.createElement(vnode.sel);
|
||||||
const elem = patch(node, vnode).elm as HTMLElement;
|
const result = patch(node, vnode);
|
||||||
function escapeTextNodes(node) {
|
return (result.elm as HTMLElement).outerHTML;
|
||||||
if (node.nodeType === 3) {
|
|
||||||
node.textContent = escape(node.textContent);
|
|
||||||
}
|
|
||||||
for (let n of node.childNodes) {
|
|
||||||
escapeTextNodes(n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
escapeTextNodes(elem);
|
|
||||||
return elem.outerHTML;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1798,6 +1798,61 @@ exports[`props evaluation arrow function prop captures loop variables 2`] = `
|
|||||||
}"
|
}"
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`props evaluation bare function calls in arrow function has rendering context as 'this' 1`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"Parent\\"
|
||||||
|
let utils = this.constructor.utils;
|
||||||
|
let QWeb = this.constructor;
|
||||||
|
let parent = context;
|
||||||
|
let scope = Object.create(context);
|
||||||
|
let h = this.h;
|
||||||
|
let c1 = [], p1 = {key:1};
|
||||||
|
let vn1 = h('div', p1, c1);
|
||||||
|
scope.ctxVal = 2;
|
||||||
|
// Component 'Child'
|
||||||
|
let w3 = '__4__' in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap['__4__']] : false;
|
||||||
|
let props3 = {callback:value=>scope['setValue'](value),value:scope['state'].val};
|
||||||
|
if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) {
|
||||||
|
w3.destroy();
|
||||||
|
w3 = false;
|
||||||
|
}
|
||||||
|
if (w3) {
|
||||||
|
w3.__updateProps(props3, extra.fiber, undefined);
|
||||||
|
let pvnode = w3.__owl__.pvnode;
|
||||||
|
c1.push(pvnode);
|
||||||
|
} else {
|
||||||
|
let componentKey3 = \`Child\`;
|
||||||
|
let W3 = scope['Child'] || context.constructor.components[componentKey3] || QWeb.components[componentKey3];
|
||||||
|
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
|
||||||
|
w3 = new W3(parent, props3);
|
||||||
|
parent.__owl__.cmap['__4__'] = w3.__owl__.id;
|
||||||
|
let fiber = w3.__prepare(extra.fiber, undefined, () => { const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||||
|
let pvnode = h('dummy', {key: '__4__', hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||||
|
c1.push(pvnode);
|
||||||
|
w3.__owl__.pvnode = pvnode;
|
||||||
|
}
|
||||||
|
w3.__owl__.parentLastFiberId = extra.fiber.id;
|
||||||
|
return vn1;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`props evaluation bare function calls in arrow function has rendering context as 'this' 2`] = `
|
||||||
|
"function anonymous(context, extra
|
||||||
|
) {
|
||||||
|
// Template name: \\"Child\\"
|
||||||
|
let scope = Object.create(context);
|
||||||
|
let h = this.h;
|
||||||
|
let c5 = [], p5 = {key:5};
|
||||||
|
let vn5 = h('span', p5, c5);
|
||||||
|
let _6 = scope['props'].value;
|
||||||
|
if (_6 != null) {
|
||||||
|
c5.push({text: _6});
|
||||||
|
}
|
||||||
|
return vn5;
|
||||||
|
}"
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`props evaluation t-set with a body expression can be used as textual prop 1`] = `
|
exports[`props evaluation t-set with a body expression can be used as textual prop 1`] = `
|
||||||
"function anonymous(context, extra
|
"function anonymous(context, extra
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -1920,6 +1920,49 @@ describe("props evaluation ", () => {
|
|||||||
expect(env.qweb.templates.Child.fn.toString()).toMatchSnapshot();
|
expect(env.qweb.templates.Child.fn.toString()).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("bare function calls in arrow function has rendering context as 'this'", async () => {
|
||||||
|
expect.assertions(7);
|
||||||
|
let child, parent;
|
||||||
|
class Child extends Component {
|
||||||
|
setup() {
|
||||||
|
child = this;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
env.qweb.addTemplate("Child", `<span><t t-esc="props.value"/></span>`);
|
||||||
|
|
||||||
|
class Parent extends Component {
|
||||||
|
static components = { Child };
|
||||||
|
state = useState({ val: 42 });
|
||||||
|
setup() {
|
||||||
|
parent = this;
|
||||||
|
}
|
||||||
|
setValue(value) {
|
||||||
|
// 'this' is the rendering context, NOT the instance
|
||||||
|
expect(this).not.toBe(parent);
|
||||||
|
// the state in the rendering context should be the same as the instance's
|
||||||
|
expect(this.state).toBe(parent.state);
|
||||||
|
expect((this as any).ctxVal).toBe(2);
|
||||||
|
this.state.val = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
env.qweb.addTemplate(
|
||||||
|
"Parent",
|
||||||
|
`<div>
|
||||||
|
<t t-set="ctxVal" t-value="2"/>
|
||||||
|
<Child callback="value => setValue(value)" value="state.val"/>
|
||||||
|
</div>`
|
||||||
|
);
|
||||||
|
|
||||||
|
const widget = new Parent();
|
||||||
|
await widget.mount(fixture);
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>42</span></div>");
|
||||||
|
child.props.callback(123);
|
||||||
|
await nextTick();
|
||||||
|
expect(fixture.innerHTML).toBe("<div><span>123</span></div>");
|
||||||
|
expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot();
|
||||||
|
expect(env.qweb.templates.Child.fn.toString()).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
test("arrow function prop captures context component instance as 'this' inside slot", async () => {
|
test("arrow function prop captures context component instance as 'this' inside slot", async () => {
|
||||||
expect.assertions(7);
|
expect.assertions(7);
|
||||||
let child, parent;
|
let child, parent;
|
||||||
|
|||||||
@@ -138,7 +138,9 @@ describe("props validation", () => {
|
|||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
expect(error).toBeDefined();
|
expect(error).toBeDefined();
|
||||||
expect(error.message).toBe("Invalid Prop 'p' in component '_a'");
|
expect(error.message).toBe(
|
||||||
|
`Invalid Prop 'p' in component '_a': type of p is not ${test.type.name}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -195,7 +197,9 @@ describe("props validation", () => {
|
|||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
expect(error).toBeDefined();
|
expect(error).toBeDefined();
|
||||||
expect(error.message).toBe("Invalid Prop 'p' in component '_a'");
|
expect(error.message).toBe(
|
||||||
|
`Invalid Prop 'p' in component '_a': type of p is not ${test.type.name}`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -240,7 +244,9 @@ describe("props validation", () => {
|
|||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
expect(error).toBeDefined();
|
expect(error).toBeDefined();
|
||||||
expect(error.message).toBe("Invalid Prop 'p' in component 'TestWidget'");
|
expect(error.message).toBe(
|
||||||
|
"Invalid Prop 'p' in component 'TestWidget': type of p is not String and type of p is not Boolean"
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can validate an optional props", async () => {
|
test("can validate an optional props", async () => {
|
||||||
@@ -284,7 +290,9 @@ describe("props validation", () => {
|
|||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
expect(error).toBeDefined();
|
expect(error).toBeDefined();
|
||||||
expect(error.message).toBe("Invalid Prop 'p' in component 'TestWidget'");
|
expect(error.message).toBe(
|
||||||
|
"Invalid Prop 'p' in component 'TestWidget': type of p is not String"
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can validate an array with given primitive type", async () => {
|
test("can validate an array with given primitive type", async () => {
|
||||||
@@ -389,7 +397,9 @@ describe("props validation", () => {
|
|||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
expect(error).toBeDefined();
|
expect(error).toBeDefined();
|
||||||
expect(error.message).toBe("Invalid Prop 'p' in component 'TestWidget'");
|
expect(error.message).toBe(
|
||||||
|
"Invalid Prop 'p' in component 'TestWidget': type of p[1] is not String and type of p[1] is not Boolean"
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can validate an object with simple shape", async () => {
|
test("can validate an object with simple shape", async () => {
|
||||||
@@ -426,7 +436,9 @@ describe("props validation", () => {
|
|||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
expect(error).toBeDefined();
|
expect(error).toBeDefined();
|
||||||
expect(error.message).toBe("Invalid prop 'p' in component TestWidget (unknown prop 'extra')");
|
expect(error.message).toBe(
|
||||||
|
"Invalid Prop 'p' in component 'TestWidget': unknown prop p['extra']"
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
props = { p: { id: "1", url: "url" } };
|
props = { p: { id: "1", url: "url" } };
|
||||||
@@ -436,7 +448,9 @@ describe("props validation", () => {
|
|||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
expect(error).toBeDefined();
|
expect(error).toBeDefined();
|
||||||
expect(error.message).toBe("Invalid Prop 'p' in component 'TestWidget'");
|
expect(error.message).toBe(
|
||||||
|
"Invalid Prop 'p' in component 'TestWidget': type of p['id'] is not Number"
|
||||||
|
);
|
||||||
|
|
||||||
error = undefined;
|
error = undefined;
|
||||||
try {
|
try {
|
||||||
@@ -447,7 +461,9 @@ describe("props validation", () => {
|
|||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
expect(error).toBeDefined();
|
expect(error).toBeDefined();
|
||||||
expect(error.message).toBe("Invalid Prop 'p' in component 'TestWidget'");
|
expect(error.message).toBe(
|
||||||
|
"Invalid Prop 'p' in component 'TestWidget': type of p['url'] is not String"
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can validate recursively complicated prop def", async () => {
|
test("can validate recursively complicated prop def", async () => {
|
||||||
@@ -499,7 +515,9 @@ describe("props validation", () => {
|
|||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
expect(error).toBeDefined();
|
expect(error).toBeDefined();
|
||||||
expect(error.message).toBe("Invalid Prop 'p' in component 'TestWidget'");
|
expect(error.message).toBe(
|
||||||
|
"Invalid Prop 'p' in component 'TestWidget': p['url'] is not an instance of Boolean and type of p['url'][1] is not Number"
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can validate optional attributes in nested sub props", () => {
|
test("can validate optional attributes in nested sub props", () => {
|
||||||
@@ -531,7 +549,7 @@ describe("props validation", () => {
|
|||||||
}
|
}
|
||||||
expect(error).toBeDefined();
|
expect(error).toBeDefined();
|
||||||
expect(error.message).toBe(
|
expect(error.message).toBe(
|
||||||
"Invalid prop 'myprop' in component TestComponent (unknown prop 'a')"
|
"Invalid Prop 'myprop' in component 'TestComponent': unknown prop myprop[0]['a']"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -557,7 +575,9 @@ describe("props validation", () => {
|
|||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
expect(error).toBeDefined();
|
expect(error).toBeDefined();
|
||||||
expect(error.message).toBe("Invalid Prop 'size' in component 'TestComponent'");
|
expect(error.message).toBe(
|
||||||
|
"Invalid Prop 'size' in component 'TestComponent': size could not be validated by `validate` function"
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("can validate with a custom validator, and a type", () => {
|
test("can validate with a custom validator, and a type", () => {
|
||||||
@@ -585,7 +605,9 @@ describe("props validation", () => {
|
|||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
expect(error).toBeDefined();
|
expect(error).toBeDefined();
|
||||||
expect(error.message).toBe("Invalid Prop 'n' in component 'TestComponent'");
|
expect(error.message).toBe(
|
||||||
|
"Invalid Prop 'n' in component 'TestComponent': type of n is not Number"
|
||||||
|
);
|
||||||
expect(validator).toBeCalledTimes(1);
|
expect(validator).toBeCalledTimes(1);
|
||||||
|
|
||||||
error = null;
|
error = null;
|
||||||
@@ -595,7 +617,9 @@ describe("props validation", () => {
|
|||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
expect(error).toBeDefined();
|
expect(error).toBeDefined();
|
||||||
expect(error.message).toBe("Invalid Prop 'n' in component 'TestComponent'");
|
expect(error.message).toBe(
|
||||||
|
"Invalid Prop 'n' in component 'TestComponent': n could not be validated by `validate` function"
|
||||||
|
);
|
||||||
expect(validator).toBeCalledTimes(2);
|
expect(validator).toBeCalledTimes(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -78,7 +78,9 @@ describe("Portal: Props validation", () => {
|
|||||||
error = e;
|
error = e;
|
||||||
}
|
}
|
||||||
expect(error).toBeDefined();
|
expect(error).toBeDefined();
|
||||||
expect(error.message).toBe(`Invalid Prop 'target' in component 'Portal'`);
|
expect(error.message).toBe(
|
||||||
|
"Invalid Prop 'target' in component 'Portal': target is not an instance of String"
|
||||||
|
);
|
||||||
|
|
||||||
QWeb.dev = dev;
|
QWeb.dev = dev;
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3111,8 +3111,7 @@ exports[`t-on t-on with inline statement, part 3 1`] = `
|
|||||||
let c1 = [], p1 = {key:1,on:{}};
|
let c1 = [], p1 = {key:1,on:{}};
|
||||||
let vn1 = h('button', p1, c1);
|
let vn1 = h('button', p1, c1);
|
||||||
const state_2 = scope['state'];
|
const state_2 = scope['state'];
|
||||||
const someFunction_2 = scope['someFunction'];
|
p1.on['click'] = function (e) {if (context.__owl__.status === 5){return}const res = (() => { return state_2.n=scope['someFunction'](3) })(); if (typeof res === 'function') { res(e) }};
|
||||||
p1.on['click'] = function (e) {if (context.__owl__.status === 5){return}const res = (() => { return state_2.n=someFunction_2(3) })(); if (typeof res === 'function') { res(e) }};
|
|
||||||
c1.push({text: \`Toggle\`});
|
c1.push({text: \`Toggle\`});
|
||||||
return vn1;
|
return vn1;
|
||||||
}"
|
}"
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ describe("t-esc", () => {
|
|||||||
test("escaping", () => {
|
test("escaping", () => {
|
||||||
qweb.addTemplate("test", `<span><t t-esc="var"/></span>`);
|
qweb.addTemplate("test", `<span><t t-esc="var"/></span>`);
|
||||||
expect(renderToString(qweb, "test", { var: "<ok>abc</ok>" })).toBe(
|
expect(renderToString(qweb, "test", { var: "<ok>abc</ok>" })).toBe(
|
||||||
"<span>&lt;ok&gt;abc&lt;/ok&gt;</span>"
|
"<span><ok>abc</ok></span>"
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user