[REF] *: reformat with prettier and printWidth=100

closes #214
This commit is contained in:
Géry Debongnie
2019-06-28 10:32:03 +02:00
parent 0095bfa61f
commit af7520d869
28 changed files with 268 additions and 948 deletions
+3
View File
@@ -60,5 +60,8 @@
"json", "json",
"node" "node"
] ]
},
"prettier": {
"printWidth": 100
} }
} }
+8 -33
View File
@@ -437,10 +437,7 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
__owl__.renderPromise = null; __owl__.renderPromise = null;
const target = __owl__.vnode || document.createElement(vnode.sel!); const target = __owl__.vnode || document.createElement(vnode.sel!);
if (this.__owl__.classObj) { if (this.__owl__.classObj) {
(<any>vnode).data.class = Object.assign( (<any>vnode).data.class = Object.assign((<any>vnode).data.class || {}, this.__owl__.classObj);
(<any>vnode).data.class || {},
this.__owl__.classObj
);
} }
__owl__.vnode = patch(target, vnode); __owl__.vnode = patch(target, vnode);
} }
@@ -471,17 +468,11 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
if (template) { if (template) {
this.template = template; this.template = template;
} else { } else {
while ( while ((template = p.name) && !(template in qweb.templates) && p !== Component) {
(template = p.name) &&
!(template in qweb.templates) &&
p !== Component
) {
p = p.__proto__; p = p.__proto__;
} }
if (p === Component) { if (p === Component) {
throw new Error( throw new Error(`Could not find template for component "${this.constructor.name}"`);
`Could not find template for component "${this.constructor.name}"`
);
} else { } else {
tmap[name] = template; tmap[name] = template;
this.template = template; this.template = template;
@@ -493,10 +484,7 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
return this.__render(); return this.__render();
} }
async __render( async __render(force: boolean = false, patchQueue: any[] = []): Promise<VNode> {
force: boolean = false,
patchQueue: any[] = []
): Promise<VNode> {
const __owl__ = this.__owl__; const __owl__ = this.__owl__;
__owl__.renderId++; __owl__.renderId++;
const promises: Promise<void>[] = []; const promises: Promise<void>[] = [];
@@ -536,10 +524,7 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
__mount(vnode: VNode, elm: HTMLElement): VNode { __mount(vnode: VNode, elm: HTMLElement): VNode {
const __owl__ = this.__owl__; const __owl__ = this.__owl__;
if (__owl__.classObj) { if (__owl__.classObj) {
(<any>vnode).data.class = Object.assign( (<any>vnode).data.class = Object.assign((<any>vnode).data.class || {}, __owl__.classObj);
(<any>vnode).data.class || {},
__owl__.classObj
);
} }
__owl__.vnode = patch(elm, vnode); __owl__.vnode = patch(elm, vnode);
if (__owl__.parent!.__owl__.isMounted && !__owl__.isMounted) { if (__owl__.parent!.__owl__.isMounted && !__owl__.isMounted) {
@@ -619,11 +604,7 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
// list of strings (prop names) // list of strings (prop names)
for (let i = 0, l = propsDef.length; i < l; i++) { for (let i = 0, l = propsDef.length; i < l; i++) {
if (!(propsDef[i] in props)) { if (!(propsDef[i] in props)) {
throw new Error( throw new Error(`Missing props '${propsDef[i]}' (component '${this.constructor.name}')`);
`Missing props '${propsDef[i]}' (component '${
this.constructor.name
}')`
);
} }
} }
} else if (propsDef) { } else if (propsDef) {
@@ -631,11 +612,7 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
for (let propName in propsDef) { for (let propName in propsDef) {
if (!(propName in props)) { if (!(propName in props)) {
if (propsDef[propName] && !propsDef[propName].optional) { if (propsDef[propName] && !propsDef[propName].optional) {
throw new Error( throw new Error(`Missing props '${propName}' (component '${this.constructor.name}')`);
`Missing props '${propName}' (component '${
this.constructor.name
}')`
);
} else { } else {
break; break;
} }
@@ -643,9 +620,7 @@ export class Component<T extends Env, Props extends {}, State extends {}> {
let isValid = isValidProp(props[propName], propsDef[propName]); let isValid = isValidProp(props[propName], propsDef[propName]);
if (!isValid) { if (!isValid) {
throw new Error( throw new Error(
`Props '${propName}' of invalid type in component '${ `Props '${propName}' of invalid type in component '${this.constructor.name}'`
this.constructor.name
}'`
); );
} }
} }
+1 -1
View File
@@ -32,7 +32,7 @@ Object.defineProperty(__info__, "mode", {
`Owl is running in 'dev' mode. This is not suitable for production use. See ${url} for more information.` `Owl is running in 'dev' mode. This is not suitable for production use. See ${url} for more information.`
); );
} else { } else {
console.log(`Owl is now running in 'prod' mode.`) console.log(`Owl is now running in 'prod' mode.`);
} }
} }
}); });
+5 -22
View File
@@ -21,15 +21,7 @@
// we define here a new modified Array prototype, which basically override all // we define here a new modified Array prototype, which basically override all
// Array methods that change some state to be able to track their changes // Array methods that change some state to be able to track their changes
const methodsToPatch = [ const methodsToPatch = ["push", "pop", "shift", "unshift", "splice", "sort", "reverse"];
"push",
"pop",
"shift",
"unshift",
"splice",
"sort",
"reverse"
];
const methodLen = methodsToPatch.length; const methodLen = methodsToPatch.length;
const ArrayProto = Array.prototype; const ArrayProto = Array.prototype;
@@ -78,18 +70,14 @@ export class Observer {
static set(target: any, key: number | string, value: any) { static set(target: any, key: number | string, value: any) {
if (!target.__owl__) { if (!target.__owl__) {
throw Error( throw Error("`Observer.set()` can only be called with observed Objects or Arrays");
"`Observer.set()` can only be called with observed Objects or Arrays"
);
} }
target.__owl__.observer.set(target, key, value); target.__owl__.observer.set(target, key, value);
} }
static delete(target: any, key: number | string) { static delete(target: any, key: number | string) {
if (!target.__owl__) { if (!target.__owl__) {
throw Error( throw Error("`Observer.delete()` can only be called with observed Objects");
"`Observer.delete()` can only be called with observed Objects"
);
} }
target.__owl__.observer.delete(target, key); target.__owl__.observer.delete(target, key);
} }
@@ -127,8 +115,7 @@ export class Observer {
set(target: any, key: number | string, value: any) { set(target: any, key: number | string, value: any) {
let alreadyDefined = let alreadyDefined =
key in target && key in target && Object.getOwnPropertyDescriptor(target, key)!.configurable === false;
Object.getOwnPropertyDescriptor(target, key)!.configurable === false;
if (alreadyDefined) { if (alreadyDefined) {
target[key] = value; target[key] = value;
} else { } else {
@@ -172,11 +159,7 @@ export class Observer {
} }
} }
_addProp<T extends { __owl__?: any }>( _addProp<T extends { __owl__?: any }>(obj: T, key: string | number, value: any) {
obj: T,
key: string | number,
value: any
) {
var self = this; var self = this;
Object.defineProperty(obj, key, { Object.defineProperty(obj, key, {
configurable: true, configurable: true,
+11 -45
View File
@@ -59,14 +59,7 @@ export interface Directive {
// Const/global stuff/helpers // Const/global stuff/helpers
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
const DISABLED_TAGS = [ const DISABLED_TAGS = ["input", "textarea", "button", "select", "option", "optgroup"];
"input",
"textarea",
"button",
"select",
"option",
"optgroup"
];
const lineBreakRE = /[\r\n]/; const lineBreakRE = /[\r\n]/;
const whitespaceRE = /\s+/g; const whitespaceRE = /\s+/g;
@@ -245,9 +238,7 @@ export class QWeb extends EventBus {
return a + b; return a + b;
}) > 1 }) > 1
) { ) {
throw new Error( throw new Error("Only one conditional branching directive is allowed per node");
"Only one conditional branching directive is allowed per node"
);
} }
// All text nodes between branch nodes are removed // All text nodes between branch nodes are removed
let textNode; let textNode;
@@ -311,28 +302,20 @@ export class QWeb extends EventBus {
} }
let template; let template;
try { try {
template = new Function( template = new Function("context", "extra", ctx.code.join("\n")) as CompiledTemplate;
"context",
"extra",
ctx.code.join("\n")
) as CompiledTemplate;
} catch (e) { } catch (e) {
const templateName = ctx.templateName.replace(/`/g, "'"); const templateName = ctx.templateName.replace(/`/g, "'");
console.groupCollapsed(`Invalid Code generated by ${templateName}`); console.groupCollapsed(`Invalid Code generated by ${templateName}`);
console.warn(ctx.code.join("\n")); console.warn(ctx.code.join("\n"));
console.groupEnd(); console.groupEnd();
throw new Error( throw new Error(
`Invalid generated code while compiling template '${templateName}': ${ `Invalid generated code while compiling template '${templateName}': ${e.message}`
e.message
}`
); );
} }
if (isDebug) { if (isDebug) {
const tpl = this.templates[name]; const tpl = this.templates[name];
if (tpl) { if (tpl) {
const msg = `Template: ${ const msg = `Template: ${tpl.elem.outerHTML}\nCompiled code:\n${template.toString()}`;
tpl.elem.outerHTML
}\nCompiled code:\n${template.toString()}`;
console.log(msg); console.log(msg);
} }
} }
@@ -481,11 +464,7 @@ export class QWeb extends EventBus {
} }
} }
_compileGenericNode( _compileGenericNode(node: ChildNode, ctx: Context, withHandlers: boolean = true): number {
node: ChildNode,
ctx: Context,
withHandlers: boolean = true
): number {
// nodeType 1 is generic tag // nodeType 1 is generic tag
if (node.nodeType !== 1) { if (node.nodeType !== 1) {
throw new Error("unsupported node type"); throw new Error("unsupported node type");
@@ -509,10 +488,7 @@ export class QWeb extends EventBus {
if (key === "disabled" && DISABLED_TAGS.indexOf(node.nodeName) > -1) { if (key === "disabled" && DISABLED_TAGS.indexOf(node.nodeName) > -1) {
isProp = true; isProp = true;
} }
if ( if ((key === "readonly" && node.nodeName === "input") || node.nodeName === "textarea") {
(key === "readonly" && node.nodeName === "input") ||
node.nodeName === "textarea"
) {
isProp = true; isProp = true;
} }
if (isProp) { if (isProp) {
@@ -526,10 +502,7 @@ export class QWeb extends EventBus {
const value = attributes[i].textContent!; const value = attributes[i].textContent!;
// regular attributes // regular attributes
if ( if (!name.startsWith("t-") && !(<Element>node).getAttribute("t-attf-" + name)) {
!name.startsWith("t-") &&
!(<Element>node).getAttribute("t-attf-" + name)
) {
const attID = ctx.generateID(); const attID = ctx.generateID();
if (name === "class") { if (name === "class") {
let classDef = value let classDef = value
@@ -577,9 +550,7 @@ export class QWeb extends EventBus {
const attValueID = ctx.generateID(); const attValueID = ctx.generateID();
ctx.addLine(`var _${attValueID} = ${formattedValue};`); ctx.addLine(`var _${attValueID} = ${formattedValue};`);
formattedValue = `'${attValue}' + (_${attValueID} ? ' ' + _${attValueID} : '')`; formattedValue = `'${attValue}' + (_${attValueID} ? ' ' + _${attValueID} : '')`;
const attrIndex = attrs.findIndex(att => const attrIndex = attrs.findIndex(att => att.startsWith(attName + ":"));
att.startsWith(attName + ":")
);
attrs.splice(attrIndex, 1); attrs.splice(attrIndex, 1);
} }
ctx.addLine(`var _${attID} = ${formattedValue};`); ctx.addLine(`var _${attID} = ${formattedValue};`);
@@ -645,9 +616,7 @@ export class QWeb extends EventBus {
ctx.addLine(`}`); ctx.addLine(`}`);
ctx.closeIf(); ctx.closeIf();
} }
ctx.addLine( ctx.addLine(`var vn${nodeID} = h('${node.nodeName}', p${nodeID}, c${nodeID});`);
`var vn${nodeID} = h('${node.nodeName}', p${nodeID}, c${nodeID});`
);
if (ctx.parentNode) { if (ctx.parentNode) {
ctx.addLine(`c${ctx.parentNode}.push(vn${nodeID});`); ctx.addLine(`c${ctx.parentNode}.push(vn${nodeID});`);
} }
@@ -776,10 +745,7 @@ export class Context {
return `(${this.formatExpression(s.slice(2, -2))})`; return `(${this.formatExpression(s.slice(2, -2))})`;
} }
let r = s.replace( let r = s.replace(/\{\{.*?\}\}/g, s => "${" + this.formatExpression(s.slice(2, -2)) + "}");
/\{\{.*?\}\}/g,
s => "${" + this.formatExpression(s.slice(2, -2)) + "}"
);
return "`" + r + "`"; return "`" + r + "`";
} }
} }
+5 -14
View File
@@ -60,9 +60,7 @@ function compileValueNode(value: any, node: Element, qweb: QWeb, ctx: Context) {
ctx.addLine(`var frag${fragID} = this.utils.getFragment(${exprID})`); ctx.addLine(`var frag${fragID} = this.utils.getFragment(${exprID})`);
let tempNodeID = ctx.generateID(); let tempNodeID = ctx.generateID();
ctx.addLine(`var p${tempNodeID} = {hook: {`); ctx.addLine(`var p${tempNodeID} = {hook: {`);
ctx.addLine( ctx.addLine(` insert: n => n.elm.parentNode.replaceChild(frag${fragID}, n.elm),`);
` insert: n => n.elm.parentNode.replaceChild(frag${fragID}, n.elm),`
);
ctx.addLine(`}};`); ctx.addLine(`}};`);
ctx.addLine(`var vn${tempNodeID} = h('div', p${tempNodeID})`); ctx.addLine(`var vn${tempNodeID} = h('div', p${tempNodeID})`);
ctx.addLine(`c${ctx.parentNode}.push(vn${tempNodeID});`); ctx.addLine(`c${ctx.parentNode}.push(vn${tempNodeID});`);
@@ -116,9 +114,7 @@ QWeb.addDirective({
if (value) { if (value) {
const formattedValue = ctx.formatExpression(value); const formattedValue = ctx.formatExpression(value);
if (ctx.variables.hasOwnProperty(variable)) { if (ctx.variables.hasOwnProperty(variable)) {
ctx.addLine( ctx.addLine(`${(<QWebExprVar>ctx.variables[variable]).id} = ${formattedValue}`);
`${(<QWebExprVar>ctx.variables[variable]).id} = ${formattedValue}`
);
} else { } else {
const varName = `_${ctx.generateID()}`; const varName = `_${ctx.generateID()}`;
ctx.addLine(`var ${varName} = ${formattedValue};`); ctx.addLine(`var ${varName} = ${formattedValue};`);
@@ -220,9 +216,7 @@ QWeb.addDirective({
} }
// compile sub template // compile sub template
const subCtx = ctx const subCtx = ctx.subContext("caller", nodeCopy).subContext("variables", Object.create(vars));
.subContext("caller", nodeCopy)
.subContext("variables", Object.create(vars));
qweb._compileNode(nodeTemplate.elem, subCtx); qweb._compileNode(nodeTemplate.elem, subCtx);
@@ -250,9 +244,7 @@ QWeb.addDirective({
const name = node.getAttribute("t-as")!; const name = node.getAttribute("t-as")!;
let arrayID = ctx.generateID(); let arrayID = ctx.generateID();
ctx.addLine(`var _${arrayID} = ${ctx.formatExpression(elems)};`); ctx.addLine(`var _${arrayID} = ${ctx.formatExpression(elems)};`);
ctx.addLine( ctx.addLine(`if (!_${arrayID}) { throw new Error('QWeb error: Invalid loop expression')}`);
`if (!_${arrayID}) { throw new Error('QWeb error: Invalid loop expression')}`
);
let keysID = ctx.generateID(); let keysID = ctx.generateID();
let valuesID = ctx.generateID(); let valuesID = ctx.generateID();
ctx.addLine(`var _${keysID} = _${valuesID} = _${arrayID};`); ctx.addLine(`var _${keysID} = _${valuesID} = _${arrayID};`);
@@ -269,8 +261,7 @@ QWeb.addDirective({
ctx.addLine(`context.${name} = _${keysID}[i];`); ctx.addLine(`context.${name} = _${keysID}[i];`);
ctx.addLine(`context.${name}_value = _${valuesID}[i];`); ctx.addLine(`context.${name}_value = _${valuesID}[i];`);
const nodeCopy = <Element>node.cloneNode(true); const nodeCopy = <Element>node.cloneNode(true);
let shouldWarn = let shouldWarn = nodeCopy.tagName !== "t" && !nodeCopy.hasAttribute("t-key");
nodeCopy.tagName !== "t" && !nodeCopy.hasAttribute("t-key");
if (!shouldWarn && node.tagName === "t") { if (!shouldWarn && node.tagName === "t") {
if (node.hasAttribute("t-component") && !node.hasAttribute("t-key")) { if (node.hasAttribute("t-component") && !node.hasAttribute("t-key")) {
shouldWarn = true; shouldWarn = true;
+3 -9
View File
@@ -82,7 +82,7 @@ const STATIC_TOKEN_MAP: { [key: string]: TKind } = {
")": "RIGHT_PAREN" ")": "RIGHT_PAREN"
}; };
const OPERATORS = ".,===,==,+,!==,!=,!,||,&&,>=,>,<=,<,?,-,*,/,%".split(','); const OPERATORS = ".,===,==,+,!==,!=,!,||,&&,>=,>,<=,<,?,-,*,/,%".split(",");
type Tokenizer = (expr: string) => Token | false; type Tokenizer = (expr: string) => Token | false;
@@ -231,10 +231,7 @@ export function tokenize(expr: string): Token[] {
* - or if the previous token is a left brace or a comma, and the next token is * - or if the previous token is a left brace or a comma, and the next token is
* a colon (in that case, this is an object key: `{a: b}`) * a colon (in that case, this is an object key: `{a: b}`)
*/ */
export function compileExpr( export function compileExpr(expr: string, vars: { [key: string]: QWebVar }): string {
expr: string,
vars: { [key: string]: QWebVar }
): string {
const tokens = tokenize(expr); const tokens = tokenize(expr);
let result = ""; let result = "";
for (let i = 0; i < tokens.length; i++) { for (let i = 0; i < tokens.length; i++) {
@@ -246,10 +243,7 @@ export function compileExpr(
if (prevToken) { if (prevToken) {
if (prevToken.type === "OPERATOR" && prevToken.value === ".") { if (prevToken.type === "OPERATOR" && prevToken.value === ".") {
isVar = false; isVar = false;
} else if ( } else if (prevToken.type === "LEFT_BRACE" || prevToken.type === "COMMA") {
prevToken.type === "LEFT_BRACE" ||
prevToken.type === "COMMA"
) {
let nextToken = tokens[i + 1]; let nextToken = tokens[i + 1];
if (nextToken && nextToken.type === "COLON") { if (nextToken && nextToken.type === "COLON") {
isVar = false; isVar = false;
+19 -64
View File
@@ -49,9 +49,7 @@ QWeb.addDirective({
)}'\`)` )}'\`)`
); );
ctx.closeIf(); ctx.closeIf();
let params = extraArgs let params = extraArgs ? `owner, ${ctx.formatExpression(extraArgs)}` : "owner";
? `owner, ${ctx.formatExpression(extraArgs)}`
: "owner";
let handler; let handler;
if (mods.length > 0) { if (mods.length > 0) {
handler = `function (e) {`; handler = `function (e) {`;
@@ -70,9 +68,7 @@ QWeb.addDirective({
ctx.addLine( ctx.addLine(
`extra.handlers['${eventName}' + ${nodeID}] = extra.handlers['${eventName}' + ${nodeID}] || ${handler};` `extra.handlers['${eventName}' + ${nodeID}] = extra.handlers['${eventName}' + ${nodeID}] || ${handler};`
); );
ctx.addLine( ctx.addLine(`p${nodeID}.on['${eventName}'] = extra.handlers['${eventName}' + ${nodeID}];`);
`p${nodeID}.on['${eventName}'] = extra.handlers['${eventName}' + ${nodeID}];`
);
} }
} }
}); });
@@ -101,9 +97,7 @@ UTILS.transitionInsert = function(vn: VNode, name: string) {
const elm = <HTMLElement>vn.elm; const elm = <HTMLElement>vn.elm;
// remove potential duplicated vnode that is currently being removed, to // remove potential duplicated vnode that is currently being removed, to
// prevent from having twice the same node in the DOM during an animation // prevent from having twice the same node in the DOM during an animation
const dup = const dup = elm.parentElement && elm.parentElement!.querySelector(`*[data-owl-key='${vn.key}']`);
elm.parentElement &&
elm.parentElement!.querySelector(`*[data-owl-key='${vn.key}']`);
if (dup) { if (dup) {
dup.remove(); dup.remove();
} }
@@ -164,9 +158,7 @@ function toMs(s: string): number {
function whenTransitionEnd(elm: HTMLElement, cb) { function whenTransitionEnd(elm: HTMLElement, cb) {
const styles = window.getComputedStyle(elm); const styles = window.getComputedStyle(elm);
const delays: Array<string> = (styles.transitionDelay || "").split(", "); const delays: Array<string> = (styles.transitionDelay || "").split(", ");
const durations: Array<string> = (styles.transitionDuration || "").split( const durations: Array<string> = (styles.transitionDuration || "").split(", ");
", "
);
const timeout: number = getTimeout(delays, durations); const timeout: number = getTimeout(delays, durations);
if (timeout > 0) { if (timeout > 0) {
elm.addEventListener("transitionend", cb, { once: true }); elm.addEventListener("transitionend", cb, { once: true });
@@ -425,9 +417,7 @@ QWeb.addDirective({
// necessary to prevent collisions // necessary to prevent collisions
if (!key && ctx.inLoop) { if (!key && ctx.inLoop) {
let id = ctx.generateID(); let id = ctx.generateID();
ctx.addLine( ctx.addLine(`let template${id} = "_slot_" + String(-${componentID} - i)`);
`let template${id} = "_slot_" + String(-${componentID} - i)`
);
templateID = `template${id}`; templateID = `template${id}`;
} else { } else {
templateID = `"_slot_${templateID}"`; templateID = `"_slot_${templateID}"`;
@@ -446,9 +436,7 @@ QWeb.addDirective({
if (transition) { if (transition) {
transitionsInsertCode = `utils.transitionInsert(vn, '${transition}');`; transitionsInsertCode = `utils.transitionInsert(vn, '${transition}');`;
} }
let finalizeComponentCode = `w${componentID}.${ let finalizeComponentCode = `w${componentID}.${keepAlive ? "unmount" : "destroy"}();`;
keepAlive ? "unmount" : "destroy"
}();`;
if (ref && !keepAlive) { if (ref && !keepAlive) {
finalizeComponentCode += `delete context.refs[${refKey}];`; finalizeComponentCode += `delete context.refs[${refKey}];`;
} }
@@ -501,9 +489,7 @@ QWeb.addDirective({
// we need to evaluate the arguments now, because the handler will // we need to evaluate the arguments now, because the handler will
// be set asynchronously later when the widget is ready, and the // be set asynchronously later when the widget is ready, and the
// context might be different. // context might be different.
ctx.addLine( ctx.addLine(`let arg${argId} = ${ctx.formatExpression(extraArgs)};`);
`let arg${argId} = ${ctx.formatExpression(extraArgs)};`
);
params = `owner, arg${argId}`; params = `owner, arg${argId}`;
} else { } else {
params = `owner, ${ctx.formatExpression(extraArgs)}`; params = `owner, ${ctx.formatExpression(extraArgs)}`;
@@ -536,9 +522,7 @@ QWeb.addDirective({
if (async) { if (async) {
ctx.addLine(`const patchQueue${componentID} = [];`); ctx.addLine(`const patchQueue${componentID} = [];`);
ctx.addLine( ctx.addLine(
`c${ `c${ctx.parentNode}.push(w${componentID} && w${componentID}.__owl__.pvnode || null);`
ctx.parentNode
}.push(w${componentID} && w${componentID}.__owl__.pvnode || null);`
); );
} else { } else {
ctx.addLine(`c${ctx.parentNode}.push(null);`); ctx.addLine(`c${ctx.parentNode}.push(null);`);
@@ -547,9 +531,7 @@ QWeb.addDirective({
ctx.addIf( ctx.addIf(
`w${componentID} && w${componentID}.__owl__.renderPromise && !w${componentID}.__owl__.vnode` `w${componentID} && w${componentID}.__owl__.renderPromise && !w${componentID}.__owl__.vnode`
); );
ctx.addIf( ctx.addIf(`utils.shallowEqual(props${componentID}, w${componentID}.__owl__.renderProps)`);
`utils.shallowEqual(props${componentID}, w${componentID}.__owl__.renderProps)`
);
ctx.addLine(`def${defID} = w${componentID}.__owl__.renderPromise;`); ctx.addLine(`def${defID} = w${componentID}.__owl__.renderPromise;`);
ctx.addElse(); ctx.addElse();
ctx.addLine(`w${componentID}.destroy();`); ctx.addLine(`w${componentID}.destroy();`);
@@ -568,12 +550,8 @@ QWeb.addDirective({
ctx.addLine( ctx.addLine(
`if (!W${componentID}) {throw new Error('Cannot find the definition of component "' + componentKey${componentID} + '"')}` `if (!W${componentID}) {throw new Error('Cannot find the definition of component "' + componentKey${componentID} + '"')}`
); );
ctx.addLine( ctx.addLine(`w${componentID} = new W${componentID}(owner, props${componentID});`);
`w${componentID} = new W${componentID}(owner, props${componentID});` ctx.addLine(`context.__owl__.cmap[${templateID}] = w${componentID}.__owl__.id;`);
);
ctx.addLine(
`context.__owl__.cmap[${templateID}] = w${componentID}.__owl__.id;`
);
// SLOTS // SLOTS
if (node.childNodes.length) { if (node.childNodes.length) {
@@ -587,11 +565,7 @@ QWeb.addDirective({
slotNode.parentElement!.removeChild(slotNode); slotNode.parentElement!.removeChild(slotNode);
const key = slotNode.getAttribute("t-set")!; const key = slotNode.getAttribute("t-set")!;
slotNode.removeAttribute("t-set"); slotNode.removeAttribute("t-set");
const slotFn = qweb._compile( const slotFn = qweb._compile(`slot_${key}_template`, slotNode, ctx.parentNode!);
`slot_${key}_template`,
slotNode,
ctx.parentNode!
);
qweb.slots[`${slotId}_${key}`] = slotFn.bind(qweb); qweb.slots[`${slotId}_${key}`] = slotFn.bind(qweb);
} }
} }
@@ -600,11 +574,7 @@ QWeb.addDirective({
for (let child of Object.values(clone.childNodes)) { for (let child of Object.values(clone.childNodes)) {
t.appendChild(child); t.appendChild(child);
} }
const slotFn = qweb._compile( const slotFn = qweb._compile(`slot_default_template`, t, ctx.parentNode!);
`slot_default_template`,
t,
ctx.parentNode!
);
qweb.slots[`${slotId}_default`] = slotFn.bind(qweb); qweb.slots[`${slotId}_default`] = slotFn.bind(qweb);
} }
} }
@@ -619,9 +589,7 @@ QWeb.addDirective({
ctx.addElse(); ctx.addElse();
// need to update component // need to update component
const patchQueueCode = async const patchQueueCode = async ? `patchQueue${componentID}` : "extra.patchQueue";
? `patchQueue${componentID}`
: "extra.patchQueue";
ctx.addLine( ctx.addLine(
`def${defID} = def${defID} || w${componentID}.__updateProps(props${componentID}, extra.forceUpdate, ${patchQueueCode});` `def${defID} = def${defID} || w${componentID}.__updateProps(props${componentID}, extra.forceUpdate, ${patchQueueCode});`
); );
@@ -650,11 +618,7 @@ QWeb.addDirective({
ctx.addLine(`extra.promises.push(def${defID});`); ctx.addLine(`extra.promises.push(def${defID});`);
} }
if ( if (node.hasAttribute("t-if") || node.hasAttribute("t-else") || node.hasAttribute("t-elif")) {
node.hasAttribute("t-if") ||
node.hasAttribute("t-else") ||
node.hasAttribute("t-elif")
) {
ctx.closeIf(); ctx.closeIf();
} }
@@ -694,10 +658,7 @@ QWeb.addDirective({
`extra.mountedHandlers[${nodeID}] = extra.mountedHandlers[${nodeID}] || (context['${handler}'] || ${error}).bind(owner);` `extra.mountedHandlers[${nodeID}] = extra.mountedHandlers[${nodeID}] || (context['${handler}'] || ${error}).bind(owner);`
); );
} }
addNodeHook( addNodeHook("insert", `if (context.__owl__.isMounted) { extra.mountedHandlers[${nodeID}](); }`);
"insert",
`if (context.__owl__.isMounted) { extra.mountedHandlers[${nodeID}](); }`
);
} }
}); });
@@ -709,9 +670,7 @@ QWeb.addDirective({
priority: 80, priority: 80,
atNodeEncounter({ ctx, value }): boolean { atNodeEncounter({ ctx, value }): boolean {
const slotKey = ctx.generateID(); const slotKey = ctx.generateID();
ctx.addLine( ctx.addLine(`const slot${slotKey} = this.slots[context.__owl__.slotId + '_' + '${value}'];`);
`const slot${slotKey} = this.slots[context.__owl__.slotId + '_' + '${value}'];`
);
ctx.addIf(`slot${slotKey}`); ctx.addIf(`slot${slotKey}`);
ctx.addLine( ctx.addLine(
`slot${slotKey}(context.__owl__.parent, Object.assign({}, extra, {parentNode: c${ `slot${slotKey}(context.__owl__.parent, Object.assign({}, extra, {parentNode: c${
@@ -747,9 +706,7 @@ QWeb.addDirective({
handler = `(ev) => {context.state['${value}'] = ev.target.checked}`; handler = `(ev) => {context.state['${value}'] = ev.target.checked}`;
} else if (type === "radio") { } else if (type === "radio") {
const nodeValue = node.getAttribute("value")!; const nodeValue = node.getAttribute("value")!;
ctx.addLine( ctx.addLine(`p${nodeID}.props = {checked:context.state['${value}'] === '${nodeValue}'};`);
`p${nodeID}.props = {checked:context.state['${value}'] === '${nodeValue}'};`
);
handler = `(ev) => {context.state['${value}'] = ev.target.value}`; handler = `(ev) => {context.state['${value}'] = ev.target.value}`;
event = "click"; event = "click";
} else { } else {
@@ -765,8 +722,6 @@ QWeb.addDirective({
ctx.addLine( ctx.addLine(
`extra.handlers['${event}' + ${nodeID}] = extra.handlers['${event}' + ${nodeID}] || (${handler});` `extra.handlers['${event}' + ${nodeID}] = extra.handlers['${event}' + ${nodeID}] || (${handler});`
); );
ctx.addLine( ctx.addLine(`p${nodeID}.on['${event}'] = extra.handlers['${event}' + ${nodeID}];`);
`p${nodeID}.on['${event}'] = extra.handlers['${event}' + ${nodeID}];`
);
} }
}); });
+1 -5
View File
@@ -308,11 +308,7 @@ export function connect<E extends EnvWithStore, P, S>(
); );
} }
__owl__.ownProps = nextProps; __owl__.ownProps = nextProps;
const mergedProps = Object.assign( const mergedProps = Object.assign({}, nextProps, __owl__.currentStoreProps);
{},
nextProps,
__owl__.currentStoreProps
);
return super.__updateProps(mergedProps, forceUpdate, patchQueue); return super.__updateProps(mergedProps, forceUpdate, patchQueue);
} }
}; };
+2 -6
View File
@@ -17,7 +17,7 @@ export function whenReady(fn) {
} else { } else {
document.addEventListener("DOMContentLoaded", resolve, false); document.addEventListener("DOMContentLoaded", resolve, false);
} }
}).then(fn || function () {}); }).then(fn || function() {});
} }
const loadedScripts: { [key: string]: Promise<void> } = {}; const loadedScripts: { [key: string]: Promise<void> } = {};
@@ -76,11 +76,7 @@ export function escape(str: string | number | undefined): string {
* *
* Inspired by https://davidwalsh.name/javascript-debounce-function * Inspired by https://davidwalsh.name/javascript-debounce-function
*/ */
export function debounce( export function debounce(func: Function, wait: number, immediate?: boolean): Function {
func: Function,
wait: number,
immediate?: boolean
): Function {
let timeout; let timeout;
return function(this: any) { return function(this: any) {
const context = this; const context = this;
+25 -118
View File
@@ -109,14 +109,7 @@ function createKeyToOldIdx(
return map; return map;
} }
const hooks: (keyof Module)[] = [ const hooks: (keyof Module)[] = ["create", "update", "remove", "destroy", "pre", "post"];
"create",
"update",
"remove",
"destroy",
"pre",
"post"
];
export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) { export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
let i: number, let i: number,
@@ -138,13 +131,7 @@ export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
function emptyNodeAt(elm: Element) { function emptyNodeAt(elm: Element) {
const id = elm.id ? "#" + elm.id : ""; const id = elm.id ? "#" + elm.id : "";
const c = elm.className ? "." + elm.className.split(" ").join(".") : ""; const c = elm.className ? "." + elm.className.split(" ").join(".") : "";
return vnode( return vnode(api.tagName(elm).toLowerCase() + id + c, {}, [], undefined, elm);
api.tagName(elm).toLowerCase() + id + c,
{},
[],
undefined,
elm
);
} }
function createRmCb(childElm: Node, listeners: number) { function createRmCb(childElm: Node, listeners: number) {
@@ -179,19 +166,14 @@ export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
const dotIdx = sel.indexOf(".", hashIdx); const dotIdx = sel.indexOf(".", hashIdx);
const hash = hashIdx > 0 ? hashIdx : sel.length; const hash = hashIdx > 0 ? hashIdx : sel.length;
const dot = dotIdx > 0 ? dotIdx : sel.length; const dot = dotIdx > 0 ? dotIdx : sel.length;
const tag = const tag = hashIdx !== -1 || dotIdx !== -1 ? sel.slice(0, Math.min(hash, dot)) : sel;
hashIdx !== -1 || dotIdx !== -1
? sel.slice(0, Math.min(hash, dot))
: sel;
const elm = (vnode.elm = const elm = (vnode.elm =
isDef(data) && isDef((i = (data as VNodeData).ns)) isDef(data) && isDef((i = (data as VNodeData).ns))
? api.createElementNS(i, tag) ? api.createElementNS(i, tag)
: api.createElement(tag)); : api.createElement(tag));
if (hash < dot) elm.setAttribute("id", sel.slice(hash + 1, dot)); if (hash < dot) elm.setAttribute("id", sel.slice(hash + 1, dot));
if (dotIdx > 0) if (dotIdx > 0) elm.setAttribute("class", sel.slice(dot + 1).replace(/\./g, " "));
elm.setAttribute("class", sel.slice(dot + 1).replace(/\./g, " ")); for (i = 0, iLen = cbs.create.length; i < iLen; ++i) cbs.create[i](emptyNode, vnode);
for (i = 0, iLen = cbs.create.length; i < iLen; ++i)
cbs.create[i](emptyNode, vnode);
if (array(children)) { if (array(children)) {
for (i = 0, iLen = children.length; i < iLen; ++i) { for (i = 0, iLen = children.length; i < iLen; ++i) {
const ch = children[i]; const ch = children[i];
@@ -237,8 +219,7 @@ export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
data = vnode.data; data = vnode.data;
if (data !== undefined) { if (data !== undefined) {
if (isDef((i = data.hook)) && isDef((i = i.destroy))) i(vnode); if (isDef((i = data.hook)) && isDef((i = i.destroy))) i(vnode);
for (i = 0, iLen = cbs.destroy.length; i < iLen; ++i) for (i = 0, iLen = cbs.destroy.length; i < iLen; ++i) cbs.destroy[i](vnode);
cbs.destroy[i](vnode);
if (vnode.children !== undefined) { if (vnode.children !== undefined) {
for (j = 0, jLen = vnode.children.length; j < jLen; ++j) { for (j = 0, jLen = vnode.children.length; j < jLen; ++j) {
i = vnode.children[j]; i = vnode.children[j];
@@ -267,13 +248,8 @@ export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
invokeDestroyHook(ch); invokeDestroyHook(ch);
listeners = cbs.remove.length + 1; listeners = cbs.remove.length + 1;
rm = createRmCb(ch.elm as Node, listeners); rm = createRmCb(ch.elm as Node, listeners);
for (i = 0, iLen = cbs.remove.length; i < iLen; ++i) for (i = 0, iLen = cbs.remove.length; i < iLen; ++i) cbs.remove[i](ch, rm);
cbs.remove[i](ch, rm); if (isDef((i = ch.data)) && isDef((i = i.hook)) && isDef((i = i.remove))) {
if (
isDef((i = ch.data)) &&
isDef((i = i.hook)) &&
isDef((i = i.remove))
) {
i(ch, rm); i(ch, rm);
} else { } else {
rm(); rm();
@@ -335,11 +311,7 @@ export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
} else if (sameVnode(oldEndVnode, newStartVnode)) { } else if (sameVnode(oldEndVnode, newStartVnode)) {
// Vnode moved left // Vnode moved left
patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue); patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue);
api.insertBefore( api.insertBefore(parentElm, oldEndVnode.elm as Node, oldStartVnode.elm as Node);
parentElm,
oldEndVnode.elm as Node,
oldStartVnode.elm as Node
);
oldEndVnode = oldCh[--oldEndIdx]; oldEndVnode = oldCh[--oldEndIdx];
newStartVnode = newCh[++newStartIdx]; newStartVnode = newCh[++newStartIdx];
} else { } else {
@@ -366,11 +338,7 @@ export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
} else { } else {
patchVnode(elmToMove, newStartVnode, insertedVnodeQueue); patchVnode(elmToMove, newStartVnode, insertedVnodeQueue);
oldCh[idxInOld] = undefined as any; oldCh[idxInOld] = undefined as any;
api.insertBefore( api.insertBefore(parentElm, elmToMove.elm as Node, oldStartVnode.elm as Node);
parentElm,
elmToMove.elm as Node,
oldStartVnode.elm as Node
);
} }
newStartVnode = newCh[++newStartIdx]; newStartVnode = newCh[++newStartIdx];
} }
@@ -379,31 +347,16 @@ export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
if (oldStartIdx <= oldEndIdx || newStartIdx <= newEndIdx) { if (oldStartIdx <= oldEndIdx || newStartIdx <= newEndIdx) {
if (oldStartIdx > oldEndIdx) { if (oldStartIdx > oldEndIdx) {
before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].elm; before = newCh[newEndIdx + 1] == null ? null : newCh[newEndIdx + 1].elm;
addVnodes( addVnodes(parentElm, before, newCh, newStartIdx, newEndIdx, insertedVnodeQueue);
parentElm,
before,
newCh,
newStartIdx,
newEndIdx,
insertedVnodeQueue
);
} else { } else {
removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx); removeVnodes(parentElm, oldCh, oldStartIdx, oldEndIdx);
} }
} }
} }
function patchVnode( function patchVnode(oldVnode: VNode, vnode: VNode, insertedVnodeQueue: VNodeQueue) {
oldVnode: VNode,
vnode: VNode,
insertedVnodeQueue: VNodeQueue
) {
let i: any, iLen: number, hook: any; let i: any, iLen: number, hook: any;
if ( if (isDef((i = vnode.data)) && isDef((hook = i.hook)) && isDef((i = hook.prepatch))) {
isDef((i = vnode.data)) &&
isDef((hook = i.hook)) &&
isDef((i = hook.prepatch))
) {
i(oldVnode, vnode); i(oldVnode, vnode);
} }
const elm = (vnode.elm = oldVnode.elm as Node); const elm = (vnode.elm = oldVnode.elm as Node);
@@ -411,20 +364,14 @@ export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
let ch = vnode.children; let ch = vnode.children;
if (oldVnode === vnode) return; if (oldVnode === vnode) return;
if (vnode.data !== undefined) { if (vnode.data !== undefined) {
for (i = 0, iLen = cbs.update.length; i < iLen; ++i) for (i = 0, iLen = cbs.update.length; i < iLen; ++i) cbs.update[i](oldVnode, vnode);
cbs.update[i](oldVnode, vnode);
i = vnode.data.hook; i = vnode.data.hook;
if (isDef(i) && isDef((i = i.update))) i(oldVnode, vnode); if (isDef(i) && isDef((i = i.update))) i(oldVnode, vnode);
} }
if (isUndef(vnode.text)) { if (isUndef(vnode.text)) {
if (isDef(oldCh) && isDef(ch)) { if (isDef(oldCh) && isDef(ch)) {
if (oldCh !== ch) if (oldCh !== ch)
updateChildren( updateChildren(elm, oldCh as Array<VNode>, ch as Array<VNode>, insertedVnodeQueue);
elm,
oldCh as Array<VNode>,
ch as Array<VNode>,
insertedVnodeQueue
);
} else if (isDef(ch)) { } else if (isDef(ch)) {
if (isDef(oldVnode.text)) api.setTextContent(elm, ""); if (isDef(oldVnode.text)) api.setTextContent(elm, "");
addVnodes( addVnodes(
@@ -436,23 +383,13 @@ export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
insertedVnodeQueue insertedVnodeQueue
); );
} else if (isDef(oldCh)) { } else if (isDef(oldCh)) {
removeVnodes( removeVnodes(elm, oldCh as Array<VNode>, 0, (oldCh as Array<VNode>).length - 1);
elm,
oldCh as Array<VNode>,
0,
(oldCh as Array<VNode>).length - 1
);
} else if (isDef(oldVnode.text)) { } else if (isDef(oldVnode.text)) {
api.setTextContent(elm, ""); api.setTextContent(elm, "");
} }
} else if (oldVnode.text !== vnode.text) { } else if (oldVnode.text !== vnode.text) {
if (isDef(oldCh)) { if (isDef(oldCh)) {
removeVnodes( removeVnodes(elm, oldCh as Array<VNode>, 0, (oldCh as Array<VNode>).length - 1);
elm,
oldCh as Array<VNode>,
0,
(oldCh as Array<VNode>).length - 1
);
} }
api.setTextContent(elm, vnode.text as string); api.setTextContent(elm, vnode.text as string);
} }
@@ -510,11 +447,7 @@ interface DOMAPI {
createElementNS: (namespaceURI: string, qualifiedName: string) => Element; createElementNS: (namespaceURI: string, qualifiedName: string) => Element;
createTextNode: (text: string) => Text; createTextNode: (text: string) => Text;
createComment: (text: string) => Comment; createComment: (text: string) => Comment;
insertBefore: ( insertBefore: (parentNode: Node, newNode: Node, referenceNode: Node | null) => void;
parentNode: Node,
newNode: Node,
referenceNode: Node | null
) => void;
removeChild: (node: Node, child: Node) => void; removeChild: (node: Node, child: Node) => void;
appendChild: (node: Node, child: Node) => void; appendChild: (node: Node, child: Node) => void;
parentNode: (node: Node) => Node; parentNode: (node: Node) => Node;
@@ -543,11 +476,7 @@ function createComment(text: string): Comment {
return document.createComment(text); return document.createComment(text);
} }
function insertBefore( function insertBefore(parentNode: Node, newNode: Node, referenceNode: Node | null): void {
parentNode: Node,
newNode: Node,
referenceNode: Node | null
): void {
parentNode.insertBefore(newNode, referenceNode); parentNode.insertBefore(newNode, referenceNode);
} }
@@ -644,21 +573,13 @@ type VNodeChildElement = VNode | string | number | undefined | null;
type ArrayOrElement<T> = T | T[]; type ArrayOrElement<T> = T | T[];
type VNodeChildren = ArrayOrElement<VNodeChildElement>; type VNodeChildren = ArrayOrElement<VNodeChildElement>;
function addNS( function addNS(data: any, children: VNodes | undefined, sel: string | undefined): void {
data: any,
children: VNodes | undefined,
sel: string | undefined
): void {
data.ns = "http://www.w3.org/2000/svg"; data.ns = "http://www.w3.org/2000/svg";
if (sel !== "foreignObject" && children !== undefined) { if (sel !== "foreignObject" && children !== undefined) {
for (let i = 0, iLen = children.length; i < iLen; ++i) { for (let i = 0, iLen = children.length; i < iLen; ++i) {
let childData = children[i].data; let childData = children[i].data;
if (childData !== undefined) { if (childData !== undefined) {
addNS( addNS(childData, (children[i] as VNode).children as VNodes, children[i].sel);
childData,
(children[i] as VNode).children as VNodes,
children[i].sel
);
} }
} }
} }
@@ -697,13 +618,7 @@ export function h(sel: any, b?: any, c?: any): VNode {
if (children !== undefined) { if (children !== undefined) {
for (i = 0, iLen = children.length; i < iLen; ++i) { for (i = 0, iLen = children.length; i < iLen; ++i) {
if (primitive(children[i])) if (primitive(children[i]))
children[i] = vnode( children[i] = vnode(undefined, undefined, undefined, children[i], undefined);
undefined,
undefined,
undefined,
children[i],
undefined
);
} }
} }
if ( if (
@@ -769,9 +684,7 @@ interface Module {
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// module/eventlisteners.ts // module/eventlisteners.ts
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
type On = { type On = { [N in keyof HTMLElementEventMap]?: (ev: HTMLElementEventMap[N]) => void } & {
[N in keyof HTMLElementEventMap]?: (ev: HTMLElementEventMap[N]) => void
} & {
[event: string]: EventListener; [event: string]: EventListener;
}; };
@@ -850,8 +763,7 @@ function updateEventListeners(oldVnode: VNode, vnode?: VNode): void {
// add new listeners which has not already attached // add new listeners which has not already attached
if (on) { if (on) {
// reuse existing listener or create new // reuse existing listener or create new
var listener = ((vnode as any).listener = var listener = ((vnode as any).listener = (oldVnode as any).listener || createListener());
(oldVnode as any).listener || createListener());
// update vnode for listener // update vnode for listener
listener.vnode = vnode; listener.vnode = vnode;
@@ -973,9 +885,4 @@ const classModule = { create: updateClass, update: updateClass } as Module;
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// patch // patch
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
export const patch = init([ export const patch = init([eventListenersModule, attrsModule, propsModule, classModule]);
eventListenersModule,
attrsModule,
propsModule,
classModule
]);
+7 -22
View File
@@ -173,10 +173,7 @@ describe("animations", () => {
test("t-transition combined with component", async () => { test("t-transition combined with component", async () => {
expect.assertions(5); expect.assertions(5);
env.qweb.addTemplate( env.qweb.addTemplate("Parent", `<div><Child t-transition="chimay"/></div>`);
"Parent",
`<div><Child t-transition="chimay"/></div>`
);
env.qweb.addTemplate("Child", `<span>blue</span>`); env.qweb.addTemplate("Child", `<span>blue</span>`);
class Parent extends Widget { class Parent extends Widget {
components = { Child: Child }; components = { Child: Child };
@@ -320,9 +317,7 @@ describe("animations", () => {
expect(spans.length).toBe(1); expect(spans.length).toBe(1);
expect(spans[0].className).toBe(`chimay-${phase} chimay-${phase}-active`); expect(spans[0].className).toBe(`chimay-${phase} chimay-${phase}-active`);
cb(); cb();
expect(spans[0].className).toBe( expect(spans[0].className).toBe(`chimay-${phase}-active chimay-${phase}-to`);
`chimay-${phase}-active chimay-${phase}-to`
);
def.resolve(); def.resolve();
}); });
@@ -330,9 +325,7 @@ describe("animations", () => {
button!.click(); button!.click();
await def; // wait for the mocked repaint to be done await def; // wait for the mocked repaint to be done
widget.el!.querySelector("span")!.dispatchEvent(new Event("transitionend")); // mock end of css transition widget.el!.querySelector("span")!.dispatchEvent(new Event("transitionend")); // mock end of css transition
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe('<div><button>Toggle</button><span class="">blue</span></div>');
'<div><button>Toggle</button><span class="">blue</span></div>'
);
// click to remove the span, and click again to re-add it before transitionend // click to remove the span, and click again to re-add it before transitionend
def = makeDeferred(); def = makeDeferred();
@@ -346,9 +339,7 @@ describe("animations", () => {
await def; // wait for the mocked repaint to be done await def; // wait for the mocked repaint to be done
widget.el!.querySelector("span")!.dispatchEvent(new Event("transitionend")); // mock end of css transition widget.el!.querySelector("span")!.dispatchEvent(new Event("transitionend")); // mock end of css transition
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe('<div><button>Toggle</button><span class="">blue</span></div>');
'<div><button>Toggle</button><span class="">blue</span></div>'
);
}); });
test("t-transition combined with t-component, remove and re-add before transitionend", async () => { test("t-transition combined with t-component, remove and re-add before transitionend", async () => {
@@ -386,9 +377,7 @@ describe("animations", () => {
expect(spans.length).toBe(1); expect(spans.length).toBe(1);
expect(spans[0].className).toBe(`chimay-${phase} chimay-${phase}-active`); expect(spans[0].className).toBe(`chimay-${phase} chimay-${phase}-active`);
cb(); cb();
expect(spans[0].className).toBe( expect(spans[0].className).toBe(`chimay-${phase}-active chimay-${phase}-to`);
`chimay-${phase}-active chimay-${phase}-to`
);
def.resolve(); def.resolve();
}); });
@@ -396,9 +385,7 @@ describe("animations", () => {
button!.click(); button!.click();
await def; // wait for the mocked repaint to be done await def; // wait for the mocked repaint to be done
widget.el!.querySelector("span")!.dispatchEvent(new Event("transitionend")); // mock end of css transition widget.el!.querySelector("span")!.dispatchEvent(new Event("transitionend")); // mock end of css transition
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe('<div><button>Toggle</button><span class="">blue</span></div>');
'<div><button>Toggle</button><span class="">blue</span></div>'
);
// click to remove the span, and click again to re-add it before transitionend // click to remove the span, and click again to re-add it before transitionend
def = makeDeferred(); def = makeDeferred();
@@ -412,8 +399,6 @@ describe("animations", () => {
await def; // wait for the mocked repaint to be done await def; // wait for the mocked repaint to be done
widget.el!.querySelector("span")!.dispatchEvent(new Event("transitionend")); // mock end of css transition widget.el!.querySelector("span")!.dispatchEvent(new Event("transitionend")); // mock end of css transition
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe('<div><button>Toggle</button><span class="">blue</span></div>');
'<div><button>Toggle</button><span class="">blue</span></div>'
);
}); });
}); });
+60 -202
View File
@@ -92,9 +92,7 @@ describe("basic widget properties", () => {
try { try {
await widget.mount(fixture); await widget.mount(fixture);
} catch (e) { } catch (e) {
expect(e.message).toBe( expect(e.message).toBe('Could not find template for component "SomeWidget"');
'Could not find template for component "SomeWidget"'
);
} }
}); });
@@ -128,9 +126,7 @@ describe("basic widget properties", () => {
class StyledWidget extends Widget {} class StyledWidget extends Widget {}
const widget = new StyledWidget(env); const widget = new StyledWidget(env);
await widget.mount(fixture); await widget.mount(fixture);
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe(`<div style="font-weight:bold;" class="some-class">world</div>`);
`<div style="font-weight:bold;" class="some-class">world</div>`
);
}); });
test("changing state before first render does not trigger a render", async () => { test("changing state before first render does not trigger a render", async () => {
@@ -249,10 +245,7 @@ describe("lifecycle hooks", () => {
"ParentWidget", "ParentWidget",
`<div><t t-if="state.flag"><t t-component="child"/></t></div>` `<div><t t-if="state.flag"><t t-component="child"/></t></div>`
); );
env.qweb.addTemplate( env.qweb.addTemplate("ChildWidget", `<div><t t-component="childchild"/></div>`);
"ChildWidget",
`<div><t t-component="childchild"/></div>`
);
class ParentWidget extends Widget { class ParentWidget extends Widget {
components = { child: ChildWidget }; components = { child: ChildWidget };
@@ -300,10 +293,7 @@ describe("lifecycle hooks", () => {
test("willPatch, patched hook are called on subsubcomponents, in proper order", async () => { test("willPatch, patched hook are called on subsubcomponents, in proper order", async () => {
const steps: any[] = []; const steps: any[] = [];
env.qweb.addTemplate( env.qweb.addTemplate("ParentWidget", `<div><t t-component="child" n="state.n"/></div>`);
"ParentWidget",
`<div><t t-component="child" n="state.n"/></div>`
);
class ParentWidget extends Widget { class ParentWidget extends Widget {
components = { child: ChildWidget }; components = { child: ChildWidget };
state = { n: 1 }; state = { n: 1 };
@@ -314,10 +304,7 @@ describe("lifecycle hooks", () => {
steps.push("parent:patched"); steps.push("parent:patched");
} }
} }
env.qweb.addTemplate( env.qweb.addTemplate("ChildWidget", `<div><t t-component="childchild" n="props.n"/></div>`);
"ChildWidget",
`<div><t t-component="childchild" n="props.n"/></div>`
);
class ChildWidget extends Widget { class ChildWidget extends Widget {
components = { childchild: ChildChildWidget }; components = { childchild: ChildChildWidget };
willPatch() { willPatch() {
@@ -780,11 +767,7 @@ describe("lifecycle hooks", () => {
expect(steps).toEqual(["child:mounted", "child:willUnmount"]); expect(steps).toEqual(["child:mounted", "child:willUnmount"]);
widget.state.flag = true; widget.state.flag = true;
await nextTick(); await nextTick();
expect(steps).toEqual([ expect(steps).toEqual(["child:mounted", "child:willUnmount", "child:mounted"]);
"child:mounted",
"child:willUnmount",
"child:mounted"
]);
}); });
}); });
@@ -859,10 +842,7 @@ describe("composition", () => {
test("can use components from the global registry", async () => { test("can use components from the global registry", async () => {
QWeb.register("WidgetB", WidgetB); QWeb.register("WidgetB", WidgetB);
env.qweb.addTemplate( env.qweb.addTemplate("ParentWidget", `<div><t t-component="WidgetB"/></div>`);
"ParentWidget",
`<div><t t-component="WidgetB"/></div>`
);
class ParentWidget extends Widget {} class ParentWidget extends Widget {}
const widget = new ParentWidget(env); const widget = new ParentWidget(env);
await widget.mount(fixture); await widget.mount(fixture);
@@ -872,10 +852,7 @@ describe("composition", () => {
test("don't fallback to global registry if widget defined locally", async () => { test("don't fallback to global registry if widget defined locally", async () => {
QWeb.register("WidgetB", WidgetB); // should not use this widget QWeb.register("WidgetB", WidgetB); // should not use this widget
env.qweb.addTemplate( env.qweb.addTemplate("ParentWidget", `<div><t t-component="WidgetB"/></div>`);
"ParentWidget",
`<div><t t-component="WidgetB"/></div>`
);
env.qweb.addTemplate("AnotherWidgetB", `<span>Belgium</span>`); env.qweb.addTemplate("AnotherWidgetB", `<span>Belgium</span>`);
class AnotherWidgetB extends Widget {} class AnotherWidgetB extends Widget {}
class ParentWidget extends Widget { class ParentWidget extends Widget {
@@ -912,17 +889,12 @@ describe("composition", () => {
try { try {
await parent.mount(fixture); await parent.mount(fixture);
} catch (e) { } catch (e) {
expect(e.message).toBe( expect(e.message).toBe('Cannot find the definition of component "SomeMispelledWidget"');
'Cannot find the definition of component "SomeMispelledWidget"'
);
} }
}); });
test("t-refs on widget are components", async () => { test("t-refs on widget are components", async () => {
env.qweb.addTemplate( env.qweb.addTemplate("WidgetC", `<div>Hello<t t-ref="mywidgetb" t-component="b"/></div>`);
"WidgetC",
`<div>Hello<t t-ref="mywidgetb" t-component="b"/></div>`
);
class WidgetC extends Widget { class WidgetC extends Widget {
components = { b: WidgetB }; components = { b: WidgetB };
} }
@@ -1007,24 +979,17 @@ describe("composition", () => {
}); });
test("modifying a sub widget", async () => { test("modifying a sub widget", async () => {
env.qweb.addTemplate( env.qweb.addTemplate("ParentWidget", `<div><t t-component="Counter"/></div>`);
"ParentWidget",
`<div><t t-component="Counter"/></div>`
);
class ParentWidget extends Widget { class ParentWidget extends Widget {
components = { Counter }; components = { Counter };
} }
const widget = new ParentWidget(env); const widget = new ParentWidget(env);
await widget.mount(fixture); await widget.mount(fixture);
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe("<div><div>0<button>Inc</button></div></div>");
"<div><div>0<button>Inc</button></div></div>"
);
const button = fixture.getElementsByTagName("button")[0]; const button = fixture.getElementsByTagName("button")[0];
await button.click(); await button.click();
await nextTick(); await nextTick();
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe("<div><div>1<button>Inc</button></div></div>");
"<div><div>1<button>Inc</button></div></div>"
);
}); });
test("refs in a loop", async () => { test("refs in a loop", async () => {
@@ -1066,10 +1031,7 @@ describe("composition", () => {
}); });
test("rerendering a widget with a sub widget", async () => { test("rerendering a widget with a sub widget", async () => {
env.qweb.addTemplate( env.qweb.addTemplate("ParentWidget", `<div><t t-component="Counter"/></div>`);
"ParentWidget",
`<div><t t-component="Counter"/></div>`
);
class ParentWidget extends Widget { class ParentWidget extends Widget {
components = { Counter }; components = { Counter };
} }
@@ -1078,20 +1040,13 @@ describe("composition", () => {
const button = fixture.getElementsByTagName("button")[0]; const button = fixture.getElementsByTagName("button")[0];
await button.click(); await button.click();
await nextTick(); await nextTick();
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe("<div><div>1<button>Inc</button></div></div>");
"<div><div>1<button>Inc</button></div></div>"
);
await widget.render(); await widget.render();
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe("<div><div>1<button>Inc</button></div></div>");
"<div><div>1<button>Inc</button></div></div>"
);
}); });
test("sub components are destroyed if no longer in dom, then recreated", async () => { test("sub components are destroyed if no longer in dom, then recreated", async () => {
env.qweb.addTemplate( env.qweb.addTemplate("ParentWidget", `<div><t t-if="state.ok"><Counter /></t></div>`);
"ParentWidget",
`<div><t t-if="state.ok"><Counter /></t></div>`
);
class ParentWidget extends Widget { class ParentWidget extends Widget {
state = { ok: true }; state = { ok: true };
components = { Counter }; components = { Counter };
@@ -1101,18 +1056,14 @@ describe("composition", () => {
const button = fixture.getElementsByTagName("button")[0]; const button = fixture.getElementsByTagName("button")[0];
await button.click(); await button.click();
await nextTick(); await nextTick();
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe("<div><div>1<button>Inc</button></div></div>");
"<div><div>1<button>Inc</button></div></div>"
);
widget.state.ok = false; widget.state.ok = false;
await nextTick(); await nextTick();
expect(fixture.innerHTML).toBe("<div></div>"); expect(fixture.innerHTML).toBe("<div></div>");
widget.state.ok = true; widget.state.ok = true;
await nextTick(); await nextTick();
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe("<div><div>0<button>Inc</button></div></div>");
"<div><div>0<button>Inc</button></div></div>"
);
}); });
test("sub components with t-keepalive are not destroyed if no longer in dom", async () => { test("sub components with t-keepalive are not destroyed if no longer in dom", async () => {
@@ -1129,9 +1080,7 @@ describe("composition", () => {
const button = fixture.getElementsByTagName("button")[0]; const button = fixture.getElementsByTagName("button")[0];
await button.click(); await button.click();
await nextTick(); await nextTick();
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe("<div><div>1<button>Inc</button></div></div>");
"<div><div>1<button>Inc</button></div></div>"
);
const counter = children(widget)[0]; const counter = children(widget)[0];
expect(counter.__owl__.isMounted).toBe(true); expect(counter.__owl__.isMounted).toBe(true);
@@ -1143,9 +1092,7 @@ describe("composition", () => {
widget.state.ok = true; widget.state.ok = true;
await nextTick(); await nextTick();
expect(counter.__owl__.isMounted).toBe(true); expect(counter.__owl__.isMounted).toBe(true);
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe("<div><div>1<button>Inc</button></div></div>");
"<div><div>1<button>Inc</button></div></div>"
);
}); });
test("sub components dom state with t-keepalive is preserved", async () => { test("sub components dom state with t-keepalive is preserved", async () => {
@@ -1320,10 +1267,7 @@ describe("composition", () => {
}); });
test("t-component with dynamic value", async () => { test("t-component with dynamic value", async () => {
env.qweb.addTemplate( env.qweb.addTemplate("ParentWidget", `<div><t t-component="{{state.widget}}"/></div>`);
"ParentWidget",
`<div><t t-component="{{state.widget}}"/></div>`
);
class ParentWidget extends Widget { class ParentWidget extends Widget {
components = { WidgetB }; components = { WidgetB };
state = { widget: "WidgetB" }; state = { widget: "WidgetB" };
@@ -1335,10 +1279,7 @@ describe("composition", () => {
}); });
test("t-component with dynamic value 2", async () => { test("t-component with dynamic value 2", async () => {
env.qweb.addTemplate( env.qweb.addTemplate("ParentWidget", `<div><t t-component="Widget{{state.widget}}"/></div>`);
"ParentWidget",
`<div><t t-component="Widget{{state.widget}}"/></div>`
);
class ParentWidget extends Widget { class ParentWidget extends Widget {
components = { WidgetB }; components = { WidgetB };
state = { widget: "B" }; state = { widget: "B" };
@@ -1352,10 +1293,7 @@ describe("composition", () => {
describe("props evaluation ", () => { describe("props evaluation ", () => {
test("explicit object prop", async () => { test("explicit object prop", async () => {
env.qweb.addTemplate( env.qweb.addTemplate("Parent", `<div><t t-component="child" value="state.val"/></div>`);
"Parent",
`<div><t t-component="child" value="state.val"/></div>`
);
class Parent extends Widget { class Parent extends Widget {
components = { child: Child }; components = { child: Child };
state = { val: 42 }; state = { val: 42 };
@@ -1379,10 +1317,7 @@ describe("props evaluation ", () => {
env.qweb.addTemplate("Child", `<span><t t-esc="props.greetings"/></span>`); env.qweb.addTemplate("Child", `<span><t t-esc="props.greetings"/></span>`);
class Child extends Widget {} class Child extends Widget {}
env.qweb.addTemplate( env.qweb.addTemplate("Parent", `<div><t t-component="child" greetings="greetings"/></div>`);
"Parent",
`<div><t t-component="child" greetings="greetings"/></div>`
);
class Parent extends Widget { class Parent extends Widget {
components = { child: Child }; components = { child: Child };
get greetings() { get greetings() {
@@ -1600,9 +1535,7 @@ describe("class and style attributes with t-component", () => {
} }
const widget = new ParentWidget(env); const widget = new ParentWidget(env);
await widget.mount(fixture); await widget.mount(fixture);
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe(`<div><div style="font-weight: bold;"></div></div>`);
`<div><div style="font-weight: bold;"></div></div>`
);
}); });
test("dynamic t-att-style is properly added and updated on widget root el", async () => { test("dynamic t-att-style is properly added and updated on widget root el", async () => {
@@ -1622,16 +1555,12 @@ describe("class and style attributes with t-component", () => {
expect(env.qweb.templates.ParentWidget.fn.toString()).toMatchSnapshot(); expect(env.qweb.templates.ParentWidget.fn.toString()).toMatchSnapshot();
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe(`<div><div style="font-size: 20px;"></div></div>`);
`<div><div style="font-size: 20px;"></div></div>`
);
widget.state.style = "font-size: 30px"; widget.state.style = "font-size: 30px";
await nextTick(); await nextTick();
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe(`<div><div style="font-size: 30px;"></div></div>`);
`<div><div style="font-size: 30px;"></div></div>`
);
}); });
}); });
@@ -1891,10 +1820,7 @@ describe("other directives with t-component", () => {
}); });
test("t-if works with t-component", async () => { test("t-if works with t-component", async () => {
env.qweb.addTemplate( env.qweb.addTemplate("ParentWidget", `<div><t t-component="child" t-if="state.flag"/></div>`);
"ParentWidget",
`<div><t t-component="child" t-if="state.flag"/></div>`
);
class ParentWidget extends Widget { class ParentWidget extends Widget {
components = { child: Child }; components = { child: Child };
state = { flag: true }; state = { flag: true };
@@ -2044,19 +1970,13 @@ describe("random stuff/miscellaneous", () => {
// in this situation, we protect against a bug that occurred: because of the // in this situation, we protect against a bug that occurred: because of the
// interplay between components and vnodes, a sub widget vnode was patched // interplay between components and vnodes, a sub widget vnode was patched
// twice. // twice.
env.qweb.addTemplate( env.qweb.addTemplate("Parent", `<div><t t-component="child" flag="state.flag"/></div>`);
"Parent",
`<div><t t-component="child" flag="state.flag"/></div>`
);
class Parent extends Widget { class Parent extends Widget {
components = { child: Child }; components = { child: Child };
state = { flag: false }; state = { flag: false };
} }
env.qweb.addTemplate( env.qweb.addTemplate("Child", `<span>abc<t t-if="props.flag">def</t></span>`);
"Child",
`<span>abc<t t-if="props.flag">def</t></span>`
);
class Child extends Widget {} class Child extends Widget {}
const widget = new Parent(env); const widget = new Parent(env);
@@ -2077,10 +1997,7 @@ describe("random stuff/miscellaneous", () => {
state = { flag: false }; state = { flag: false };
} }
env.qweb.addTemplate( env.qweb.addTemplate("Child", `<span>abc<t t-if="props.flag">def</t></span>`);
"Child",
`<span>abc<t t-if="props.flag">def</t></span>`
);
class Child extends Widget {} class Child extends Widget {}
const widget = new Parent(env); const widget = new Parent(env);
@@ -2190,9 +2107,7 @@ describe("random stuff/miscellaneous", () => {
const a = new A(env); const a = new A(env);
await a.mount(fixture); await a.mount(fixture);
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe(`<div>A<div>B</div><div>C<div>D</div><div>E</div></div></div>`);
`<div>A<div>B</div><div>C<div>D</div><div>E</div></div></div>`
);
expect(steps).toEqual([ expect(steps).toEqual([
"A:willStart", "A:willStart",
"A:render", "A:render",
@@ -2267,10 +2182,7 @@ describe("async rendering", () => {
test("destroying/recreating a subwidget with different props (if start is not over)", async () => { test("destroying/recreating a subwidget with different props (if start is not over)", async () => {
let def = makeDeferred(); let def = makeDeferred();
let n = 0; let n = 0;
env.qweb.addTemplate( env.qweb.addTemplate("W", `<div><t t-if="state.val > 1"><Child val="state.val"/></t></div>`);
"W",
`<div><t t-if="state.val > 1"><Child val="state.val"/></t></div>`
);
class W extends Widget { class W extends Widget {
components = { Child }; components = { Child };
state = { val: 1 }; state = { val: 1 };
@@ -2380,23 +2292,15 @@ describe("async rendering", () => {
} }
const parent = new Parent(env); const parent = new Parent(env);
await parent.mount(fixture); await parent.mount(fixture);
expect(fixture.innerHTML.replace(/\r?\n|\r|\s+/g, "")).toBe( expect(fixture.innerHTML.replace(/\r?\n|\r|\s+/g, "")).toBe("<div><span>a1</span></div>");
"<div><span>a1</span></div>"
);
parent.state.valA = 2; parent.state.valA = 2;
await nextTick(); await nextTick();
expect(fixture.innerHTML.replace(/\r?\n|\r|\s+/g, "")).toBe( expect(fixture.innerHTML.replace(/\r?\n|\r|\s+/g, "")).toBe("<div><span>a1</span></div>");
"<div><span>a1</span></div>"
);
parent.state.flagB = true; parent.state.flagB = true;
await nextTick(); await nextTick();
expect(fixture.innerHTML.replace(/\r?\n|\r|\s+/g, "")).toBe( expect(fixture.innerHTML.replace(/\r?\n|\r|\s+/g, "")).toBe("<div><span>a1</span></div>");
"<div><span>a1</span></div>"
);
defB.resolve(); defB.resolve();
expect(fixture.innerHTML.replace(/\r?\n|\r|\s+/g, "")).toBe( expect(fixture.innerHTML.replace(/\r?\n|\r|\s+/g, "")).toBe("<div><span>a1</span></div>");
"<div><span>a1</span></div>"
);
defA.resolve(); defA.resolve();
await nextTick(); await nextTick();
expect(fixture.innerHTML.replace(/\r?\n|\r|\s+/g, "")).toBe( expect(fixture.innerHTML.replace(/\r?\n|\r|\s+/g, "")).toBe(
@@ -2569,25 +2473,19 @@ describe("async rendering", () => {
await parent.mount(fixture); await parent.mount(fixture);
expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot(); expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot();
expect(fixture.querySelector(".children")!.innerHTML).toBe( expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>0</span><span>0</span>");
"<span>0</span><span>0</span>"
);
// click on button to increment Parent counter // click on button to increment Parent counter
def = makeDeferred(); def = makeDeferred();
fixture.querySelector("button")!.click(); fixture.querySelector("button")!.click();
await nextTick(); await nextTick();
expect(fixture.querySelector(".children")!.innerHTML).toBe( expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>1</span><span>0</span>");
"<span>1</span><span>0</span>"
);
def.resolve(); def.resolve();
await nextTick(); await nextTick();
expect(fixture.querySelector(".children")!.innerHTML).toBe( expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>1</span><span>1</span>");
"<span>1</span><span>1</span>"
);
}); });
test("fast component with t-asyncroot directive", async () => { test("fast component with t-asyncroot directive", async () => {
@@ -2624,25 +2522,19 @@ describe("async rendering", () => {
await parent.mount(fixture); await parent.mount(fixture);
expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot(); expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot();
expect(fixture.querySelector(".children")!.innerHTML).toBe( expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>0</span><span>0</span>");
"<span>0</span><span>0</span>"
);
// click on button to increment Parent counter // click on button to increment Parent counter
def = makeDeferred(); def = makeDeferred();
fixture.querySelector("button")!.click(); fixture.querySelector("button")!.click();
await nextTick(); await nextTick();
expect(fixture.querySelector(".children")!.innerHTML).toBe( expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>1</span><span>0</span>");
"<span>1</span><span>0</span>"
);
def.resolve(); def.resolve();
await nextTick(); await nextTick();
expect(fixture.querySelector(".children")!.innerHTML).toBe( expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>1</span><span>1</span>");
"<span>1</span><span>1</span>"
);
}); });
test("t-component with t-asyncroot directive: mixed re-renderings", async () => { test("t-component with t-asyncroot directive: mixed re-renderings", async () => {
@@ -2687,40 +2579,30 @@ describe("async rendering", () => {
await parent.mount(fixture); await parent.mount(fixture);
expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot(); expect(env.qweb.templates.Parent.fn.toString()).toMatchSnapshot();
expect(fixture.querySelector(".children")!.innerHTML).toBe( expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>0/0</span><span>0/0</span>");
"<span>0/0</span><span>0/0</span>"
);
// click on button to increment Parent counter // click on button to increment Parent counter
def = makeDeferred(); def = makeDeferred();
fixture.querySelector("button")!.click(); fixture.querySelector("button")!.click();
await nextTick(); await nextTick();
expect(fixture.querySelector(".children")!.innerHTML).toBe( expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>0/1</span><span>0/0</span>");
"<span>0/1</span><span>0/0</span>"
);
// click on each Child to increment their local counter // click on each Child to increment their local counter
const children = parent.el!.querySelectorAll("span"); const children = parent.el!.querySelectorAll("span");
children[0]!.click(); children[0]!.click();
await nextTick(); await nextTick();
expect(fixture.querySelector(".children")!.innerHTML).toBe( expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>1/1</span><span>0/0</span>");
"<span>1/1</span><span>0/0</span>"
);
children[1]!.click(); children[1]!.click();
await nextTick(); await nextTick();
expect(fixture.querySelector(".children")!.innerHTML).toBe( expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>1/1</span><span>1/0</span>");
"<span>1/1</span><span>1/0</span>"
);
// finalize first re-rendering (coming from the props update) // finalize first re-rendering (coming from the props update)
def.resolve(); def.resolve();
await nextTick(); await nextTick();
expect(fixture.querySelector(".children")!.innerHTML).toBe( expect(fixture.querySelector(".children")!.innerHTML).toBe("<span>1/1</span><span>1/1</span>");
"<span>1/1</span><span>1/1</span>"
);
}); });
}); });
@@ -2860,9 +2742,7 @@ describe("widget and observable state", () => {
try { try {
await parent.mount(fixture); await parent.mount(fixture);
} catch (e) { } catch (e) {
expect(e.message).toBe( expect(e.message).toBe('Observed state cannot be changed here! (key: "coffee", val: "2")');
'Observed state cannot be changed here! (key: "coffee", val: "2")'
);
} }
}); });
}); });
@@ -2903,10 +2783,7 @@ describe("t-mounted directive", () => {
}); });
test("combined with a t-if", async () => { test("combined with a t-if", async () => {
env.qweb.addTemplate( env.qweb.addTemplate("TestWidget", `<div><input t-if="state.flag" t-mounted="f"/></div>`);
"TestWidget",
`<div><input t-if="state.flag" t-mounted="f"/></div>`
);
class TestWidget extends Widget { class TestWidget extends Widget {
state = { flag: false }; state = { flag: false };
f() {} f() {}
@@ -2922,10 +2799,7 @@ describe("t-mounted directive", () => {
}); });
test("combined with a t-ref", async () => { test("combined with a t-ref", async () => {
env.qweb.addTemplate( env.qweb.addTemplate("TestWidget", `<div><input t-ref="input" t-mounted="f"/></div>`);
"TestWidget",
`<div><input t-ref="input" t-mounted="f"/></div>`
);
class TestWidget extends Widget { class TestWidget extends Widget {
f() {} f() {}
} }
@@ -3099,9 +2973,7 @@ describe("t-slot directive", () => {
const parent = new Parent(env); const parent = new Parent(env);
await parent.mount(fixture); await parent.mount(fixture);
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe("<div><div><span>sts rocks</span></div></div>");
"<div><div><span>sts rocks</span></div></div>"
);
}); });
test("default slot work with text nodes", async () => { test("default slot work with text nodes", async () => {
@@ -3144,9 +3016,7 @@ describe("t-slot directive", () => {
const parent = new Parent(env); const parent = new Parent(env);
await parent.mount(fixture); await parent.mount(fixture);
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe("<div><div><span>sts</span><span>rocks</span></div></div>");
"<div><div><span>sts</span><span>rocks</span></div></div>"
);
}); });
test("multiple roots are allowed in a default slot", async () => { test("multiple roots are allowed in a default slot", async () => {
@@ -3168,9 +3038,7 @@ describe("t-slot directive", () => {
const parent = new Parent(env); const parent = new Parent(env);
await parent.mount(fixture); await parent.mount(fixture);
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe("<div><div><span>sts</span><span>rocks</span></div></div>");
"<div><div><span>sts</span><span>rocks</span></div></div>"
);
}); });
test("missing slots are ignored", async () => { test("missing slots are ignored", async () => {
@@ -3193,9 +3061,7 @@ describe("t-slot directive", () => {
const parent = new Parent(env); const parent = new Parent(env);
await parent.mount(fixture); await parent.mount(fixture);
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe("<div><span><span>some content</span></span></div>");
"<div><span><span>some content</span></span></div>"
);
}); });
test("t-debug on a t-set (defining a slot)", async () => { test("t-debug on a t-set (defining a slot)", async () => {
@@ -3264,16 +3130,12 @@ describe("t-model directive", () => {
const comp = new SomeComponent(env); const comp = new SomeComponent(env);
await comp.mount(fixture); await comp.mount(fixture);
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe('<div><input type="checkbox"><span>no</span></div>');
'<div><input type="checkbox"><span>no</span></div>'
);
let input = fixture.querySelector("input")!; let input = fixture.querySelector("input")!;
input.click(); input.click();
await nextTick(); await nextTick();
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe('<div><input type="checkbox"><span>yes</span></div>');
'<div><input type="checkbox"><span>yes</span></div>'
);
expect(comp.state.flag).toBe(true); expect(comp.state.flag).toBe(true);
expect(env.qweb.templates.SomeComponent.fn.toString()).toMatchSnapshot(); expect(env.qweb.templates.SomeComponent.fn.toString()).toMatchSnapshot();
@@ -3296,16 +3158,12 @@ describe("t-model directive", () => {
const comp = new SomeComponent(env); const comp = new SomeComponent(env);
await comp.mount(fixture); await comp.mount(fixture);
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe("<div><textarea></textarea><span></span></div>");
"<div><textarea></textarea><span></span></div>"
);
const textarea = fixture.querySelector("textarea")!; const textarea = fixture.querySelector("textarea")!;
await editInput(textarea, "test"); await editInput(textarea, "test");
expect(comp.state.text).toBe("test"); expect(comp.state.text).toBe("test");
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe("<div><textarea></textarea><span>test</span></div>");
"<div><textarea></textarea><span>test</span></div>"
);
}); });
test("on an input type=radio", async () => { test("on an input type=radio", async () => {
+3 -8
View File
@@ -52,14 +52,10 @@ interface FileData {
sections: MarkDownSection[]; sections: MarkDownSection[];
} }
function isLinkValid( function isLinkValid(link: MarkDownLink, current: FileData, files: FileData[]): boolean {
link: MarkDownLink,
current: FileData,
files: FileData[]
): boolean {
const parts = link.link.split("#"); const parts = link.link.split("#");
const currentParts = current.name.split("/"); const currentParts = current.name.split("/");
const path = currentParts.length > 1 ? currentParts[0] + '/' : ""; const path = currentParts.length > 1 ? currentParts[0] + "/" : "";
const fullName = path + parts[0]; const fullName = path + parts[0];
if (parts.length === 1) { if (parts.length === 1) {
// no # in url // no # in url
@@ -70,8 +66,7 @@ function isLinkValid(
} }
} }
} else { } else {
const file = const file = parts[0] === "" ? current : files.find(f => f.name === fullName);
parts[0] === "" ? current : files.find(f => f.name === fullName);
if (!file) { if (!file) {
return false; return false;
} }
+1 -3
View File
@@ -23,9 +23,7 @@ describe("event bus behaviour", () => {
test("throw error if callback is undefined", () => { test("throw error if callback is undefined", () => {
expect.assertions(1); expect.assertions(1);
const bus = new EventBus(); const bus = new EventBus();
expect(() => bus.on("event", {}, <any>undefined)).toThrow( expect(() => bus.on("event", {}, <any>undefined)).toThrow(`Missing callback`);
`Missing callback`
);
}); });
test("can unsubscribe", () => { test("can unsubscribe", () => {
+2 -9
View File
@@ -68,11 +68,7 @@ export function renderToDOM(
return result.elm as HTMLElement; return result.elm as HTMLElement;
} }
export function renderToString( export function renderToString(qweb: QWeb, t: string, context: EvalContext = {}): string {
qweb: QWeb,
t: string,
context: EvalContext = {}
): string {
const node = renderToDOM(qweb, t, context); const node = renderToDOM(qweb, t, context);
return node instanceof Text ? node.textContent! : node.outerHTML; return node instanceof Text ? node.textContent! : node.outerHTML;
} }
@@ -92,10 +88,7 @@ export function unpatchNextFrame() {
UTILS.nextFrame = nextFrame; UTILS.nextFrame = nextFrame;
} }
export async function editInput( export async function editInput(input: HTMLInputElement | HTMLTextAreaElement, value: string) {
input: HTMLInputElement | HTMLTextAreaElement,
value: string
) {
input.value = value; input.value = value;
input.dispatchEvent(new Event("input")); input.dispatchEvent(new Event("input"));
input.dispatchEvent(new Event("change")); input.dispatchEvent(new Event("change"));
+7 -12
View File
@@ -126,7 +126,6 @@ describe("props validation", () => {
} }
}); });
test("can validate a prop with multiple types", async () => { test("can validate a prop with multiple types", async () => {
let TestWidget = class extends Widget { let TestWidget = class extends Widget {
static props = { p: [String, Boolean] }; static props = { p: [String, Boolean] };
@@ -149,16 +148,14 @@ describe("props validation", () => {
expect(() => { expect(() => {
new TestWidget(env, { p: "hey" }); new TestWidget(env, { p: "hey" });
new TestWidget(env, { }); new TestWidget(env, {});
}).not.toThrow(); }).not.toThrow();
expect(() => { expect(() => {
new TestWidget(env, { p: 1 }); new TestWidget(env, { p: 1 });
}).toThrow(); }).toThrow();
}); });
test("can validate an array with given primitive type", async () => { test("can validate an array with given primitive type", async () => {
let TestWidget = class extends Widget { let TestWidget = class extends Widget {
static props = { p: { type: Array, element: String } }; static props = { p: { type: Array, element: String } };
@@ -222,7 +219,7 @@ describe("props validation", () => {
type: Object, type: Object,
shape: { shape: {
id: Number, id: Number,
url: [Boolean, {type: Array, element: Number}], url: [Boolean, { type: Array, element: Number }]
} }
} }
}; };
@@ -230,21 +227,19 @@ describe("props validation", () => {
expect(() => { expect(() => {
new TestWidget(env, { p: { id: 1, url: true } }); new TestWidget(env, { p: { id: 1, url: true } });
new TestWidget(env, { p: { id: 1, url: [12]} }); new TestWidget(env, { p: { id: 1, url: [12] } });
}).not.toThrow(); }).not.toThrow();
expect(() => { expect(() => {
new TestWidget(env, { p: { id: 1, url: [12, true]} }); new TestWidget(env, { p: { id: 1, url: [12, true] } });
}).toThrow(); }).toThrow();
}); });
}); });
describe("default props", () => { describe("default props", () => {
test("can set default values", async () => { test("can set default values", async () => {
class TestWidget extends Widget { class TestWidget extends Widget {
static defaultProps = {p: 4} static defaultProps = { p: 4 };
} }
const w = new TestWidget(env, {}); const w = new TestWidget(env, {});
@@ -253,10 +248,10 @@ describe("default props", () => {
test("default values are also set whenever component is updated", async () => { test("default values are also set whenever component is updated", async () => {
class TestWidget extends Widget { class TestWidget extends Widget {
static defaultProps = {p: 4} static defaultProps = { p: 4 };
} }
const w = new TestWidget(env, {p: 1}); const w = new TestWidget(env, { p: 1 });
await w.__updateProps({}); await w.__updateProps({});
expect(w.props.p).toBe(4); expect(w.props.p).toBe(4);
}); });
+35 -111
View File
@@ -26,9 +26,7 @@ describe("static templates", () => {
test("simple dynamic value", () => { test("simple dynamic value", () => {
qweb.addTemplate("test", '<t><t t-esc="text"/></t>'); qweb.addTemplate("test", '<t><t t-esc="text"/></t>');
expect(renderToString(qweb, "test", { text: "hello vdom" })).toBe( expect(renderToString(qweb, "test", { text: "hello vdom" })).toBe("hello vdom");
"hello vdom"
);
}); });
test("simple string, with some dynamic value", () => { test("simple string, with some dynamic value", () => {
@@ -54,9 +52,7 @@ describe("static templates", () => {
describe("error handling", () => { describe("error handling", () => {
test("invalid xml", () => { test("invalid xml", () => {
expect(() => qweb.addTemplate("test", "<div>")).toThrow( expect(() => qweb.addTemplate("test", "<div>")).toThrow("Invalid XML in template");
"Invalid XML in template"
);
}); });
test("template with text node and tag", () => { test("template with text node and tag", () => {
@@ -73,9 +69,7 @@ describe("error handling", () => {
test("cannot add twice the same template", () => { test("cannot add twice the same template", () => {
qweb.addTemplate("test", `<t></t>`); qweb.addTemplate("test", `<t></t>`);
expect(() => qweb.addTemplate("test", "<div/>", true)).not.toThrow( expect(() => qweb.addTemplate("test", "<div/>", true)).not.toThrow("already defined");
"already defined"
);
expect(() => qweb.addTemplate("test", "<div/>")).toThrow("already defined"); expect(() => qweb.addTemplate("test", "<div/>")).toThrow("already defined");
}); });
@@ -94,19 +88,14 @@ describe("error handling", () => {
test("nice error when t-on is evaluated with a missing event", () => { test("nice error when t-on is evaluated with a missing event", () => {
qweb.addTemplate("templatename", `<div t-on="somemethod"></div>`); qweb.addTemplate("templatename", `<div t-on="somemethod"></div>`);
expect(() => expect(() => qweb.render("templatename", { someMethod() {} }, { handlers: [] })).toThrow(
qweb.render("templatename", { someMethod() {} }, { handlers: [] }) "Missing event name with t-on directive"
).toThrow("Missing event name with t-on directive"); );
}); });
test("error when unknown directive", () => { test("error when unknown directive", () => {
qweb.addTemplate( qweb.addTemplate("templatename", `<div t-best-beer="rochefort 10">test</div>`);
"templatename", expect(() => qweb.render("templatename")).toThrow("Unknown QWeb directive: 't-best-beer'");
`<div t-best-beer="rochefort 10">test</div>`
);
expect(() => qweb.render("templatename")).toThrow(
"Unknown QWeb directive: 't-best-beer'"
);
}); });
}); });
@@ -123,9 +112,7 @@ describe("t-esc", () => {
test.skip("escaping", () => { test.skip("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>" })).toBe( expect(renderToString(qweb, "test", { var: "<ok>" })).toBe("<span>&lt;ok&gt;</span>");
"<span>&lt;ok&gt;</span>"
);
}); });
test("escaping on a node", () => { test("escaping on a node", () => {
@@ -157,9 +144,7 @@ describe("t-raw", () => {
test("not escaping", () => { test("not escaping", () => {
qweb.addTemplate("test", `<div><t t-raw="var"/></div>`); qweb.addTemplate("test", `<div><t t-raw="var"/></div>`);
expect(renderToString(qweb, "test", { var: "<ok></ok>" })).toBe( expect(renderToString(qweb, "test", { var: "<ok></ok>" })).toBe("<div><ok></ok></div>");
"<div><ok></ok></div>"
);
}); });
test("t-raw and another sibling node", () => { test("t-raw and another sibling node", () => {
@@ -172,10 +157,7 @@ describe("t-raw", () => {
describe("t-set", () => { describe("t-set", () => {
test("set from attribute literal", () => { test("set from attribute literal", () => {
qweb.addTemplate( qweb.addTemplate("test", `<div><t t-set="value" t-value="'ok'"/><t t-esc="value"/></div>`);
"test",
`<div><t t-set="value" t-value="'ok'"/><t t-esc="value"/></div>`
);
expect(renderToString(qweb, "test")).toBe("<div>ok</div>"); expect(renderToString(qweb, "test")).toBe("<div>ok</div>");
}); });
@@ -187,24 +169,16 @@ describe("t-set", () => {
<t t-if="v === 'ok'">grimbergen</t> <t t-if="v === 'ok'">grimbergen</t>
</div>` </div>`
); );
expect(renderToString(qweb, "test", { value: "ok" })).toBe( expect(renderToString(qweb, "test", { value: "ok" })).toBe("<div>grimbergen</div>");
"<div>grimbergen</div>"
);
}); });
test("set from body literal", () => { test("set from body literal", () => {
qweb.addTemplate( qweb.addTemplate("test", `<t><t t-set="value">ok</t><t t-esc="value"/></t>`);
"test",
`<t><t t-set="value">ok</t><t t-esc="value"/></t>`
);
expect(renderToString(qweb, "test")).toBe("ok"); expect(renderToString(qweb, "test")).toBe("ok");
}); });
test("set from attribute lookup", () => { test("set from attribute lookup", () => {
qweb.addTemplate( qweb.addTemplate("test", `<div><t t-set="stuff" t-value="value"/><t t-esc="stuff"/></div>`);
"test",
`<div><t t-set="stuff" t-value="value"/><t t-esc="stuff"/></div>`
);
expect(renderToString(qweb, "test", { value: "ok" })).toBe("<div>ok</div>"); expect(renderToString(qweb, "test", { value: "ok" })).toBe("<div>ok</div>");
}); });
@@ -236,18 +210,12 @@ describe("t-set", () => {
}); });
test("value priority", () => { test("value priority", () => {
qweb.addTemplate( qweb.addTemplate("test", `<div><t t-set="value" t-value="1">2</t><t t-esc="value"/></div>`);
"test",
`<div><t t-set="value" t-value="1">2</t><t t-esc="value"/></div>`
);
expect(renderToString(qweb, "test")).toBe("<div>1</div>"); expect(renderToString(qweb, "test")).toBe("<div>1</div>");
}); });
test("evaluate value expression", () => { test("evaluate value expression", () => {
qweb.addTemplate( qweb.addTemplate("test", `<div><t t-set="value" t-value="1 + 2"/><t t-esc="value"/></div>`);
"test",
`<div><t t-set="value" t-value="1 + 2"/><t t-esc="value"/></div>`
);
expect(renderToString(qweb, "test")).toBe("<div>3</div>"); expect(renderToString(qweb, "test")).toBe("<div>3</div>");
}); });
@@ -272,25 +240,19 @@ describe("t-set", () => {
"test", "test",
`<div><t t-set="value" t-value="somevariable + 2"/><t t-esc="value"/></div>` `<div><t t-set="value" t-value="somevariable + 2"/><t t-esc="value"/></div>`
); );
expect(renderToString(qweb, "test", { somevariable: 43 })).toBe( expect(renderToString(qweb, "test", { somevariable: 43 })).toBe("<div>45</div>");
"<div>45</div>"
);
}); });
}); });
describe("t-if", () => { describe("t-if", () => {
test("boolean value true condition", () => { test("boolean value true condition", () => {
qweb.addTemplate("test", `<div><t t-if="condition">ok</t></div>`); qweb.addTemplate("test", `<div><t t-if="condition">ok</t></div>`);
expect(renderToString(qweb, "test", { condition: true })).toBe( expect(renderToString(qweb, "test", { condition: true })).toBe("<div>ok</div>");
"<div>ok</div>"
);
}); });
test("boolean value false condition", () => { test("boolean value false condition", () => {
qweb.addTemplate("test", `<div><t t-if="condition">ok</t></div>`); qweb.addTemplate("test", `<div><t t-if="condition">ok</t></div>`);
expect(renderToString(qweb, "test", { condition: false })).toBe( expect(renderToString(qweb, "test", { condition: false })).toBe("<div></div>");
"<div></div>"
);
}); });
test("boolean value condition missing", () => { test("boolean value condition missing", () => {
@@ -307,9 +269,7 @@ describe("t-if", () => {
<t t-else="">beer</t></div> <t t-else="">beer</t></div>
` `
); );
expect(renderToString(qweb, "test", { color: "red" })).toBe( expect(renderToString(qweb, "test", { color: "red" })).toBe("<div>red is dead</div>");
"<div>red is dead</div>"
);
}); });
test("boolean value condition else", () => { test("boolean value condition else", () => {
@@ -335,9 +295,7 @@ describe("t-if", () => {
` `
); );
const result = trim(renderToString(qweb, "test", { condition: false })); const result = trim(renderToString(qweb, "test", { condition: false }));
expect(result).toBe( expect(result).toBe("<div><span>begin</span>fail-else<span>end</span></div>");
"<div><span>begin</span>fail-else<span>end</span></div>"
);
}); });
test("can use some boolean operators in expressions", () => { test("can use some boolean operators in expressions", () => {
@@ -362,9 +320,7 @@ describe("t-if", () => {
m: 5, m: 5,
n: 2 n: 2
}; };
expect(normalize(renderToString(qweb, "test", context))).toBe( expect(normalize(renderToString(qweb, "test", context))).toBe("<div>andormgtnlt</div>");
"<div>andormgtnlt</div>"
);
}); });
}); });
@@ -489,10 +445,7 @@ describe("attributes", () => {
}); });
test("format multiple", () => { test("format multiple", () => {
qweb.addTemplate( qweb.addTemplate("test", `<div t-attf-foo="a {{value1}} is {{value2}} of {{value3}} ]"/>`);
"test",
`<div t-attf-foo="a {{value1}} is {{value2}} of {{value3}} ]"/>`
);
const result = renderToString(qweb, "test", { const result = renderToString(qweb, "test", {
value1: 0, value1: 0,
value2: 1, value2: 1,
@@ -528,19 +481,13 @@ describe("attributes", () => {
}); });
test("class and t-attf-class with ternary operation", () => { test("class and t-attf-class with ternary operation", () => {
qweb.addTemplate( qweb.addTemplate("test", `<div class="hello" t-attf-class="{{value ? 'world' : ''}}"/>`);
"test",
`<div class="hello" t-attf-class="{{value ? 'world' : ''}}"/>`
);
const result = renderToString(qweb, "test", { value: true }); const result = renderToString(qweb, "test", { value: true });
expect(result).toBe(`<div class="hello world"></div>`); expect(result).toBe(`<div class="hello world"></div>`);
}); });
test("t-att-class with object", () => { test("t-att-class with object", () => {
qweb.addTemplate( qweb.addTemplate("test", `<div class="static" t-att-class="{a: b, c: d, e: f}"/>`);
"test",
`<div class="static" t-att-class="{a: b, c: d, e: f}"/>`
);
const result = renderToString(qweb, "test", { b: true, d: false, f: true }); const result = renderToString(qweb, "test", { b: true, d: false, f: true });
expect(result).toBe(`<div class="static a e"></div>`); expect(result).toBe(`<div class="static a e"></div>`);
}); });
@@ -569,10 +516,7 @@ describe("t-call (template calling", () => {
test("with unused setbody", () => { test("with unused setbody", () => {
qweb.addTemplate("_basic-callee", "<div>ok</div>"); qweb.addTemplate("_basic-callee", "<div>ok</div>");
qweb.addTemplate( qweb.addTemplate("caller", '<t t-call="_basic-callee"><t t-set="qux" t-value="3"/></t>');
"caller",
'<t t-call="_basic-callee"><t t-set="qux" t-value="3"/></t>'
);
const expected = "<div>ok</div>"; const expected = "<div>ok</div>";
expect(renderToString(qweb, "caller")).toBe(expected); expect(renderToString(qweb, "caller")).toBe(expected);
}); });
@@ -678,9 +622,7 @@ describe("foreach", () => {
</t> </t>
</div>` </div>`
); );
const result = trim( const result = trim(renderToString(qweb, "test", { value: { a: 1, b: 2, c: 3 } }));
renderToString(qweb, "test", { value: { a: 1, b: 2, c: 3 } })
);
const expected = `<div>[0:a1][1:b2][2:c3]</div>`; const expected = `<div>[0:a1][1:b2][2:c3]</div>`;
expect(result).toBe(expected); expect(result).toBe(expected);
}); });
@@ -727,14 +669,8 @@ describe("foreach", () => {
describe("misc", () => { describe("misc", () => {
test("global", () => { test("global", () => {
qweb.addTemplate("_callee-asc", `<Año t-att-falló="'agüero'" t-raw="0"/>`); qweb.addTemplate("_callee-asc", `<Año t-att-falló="'agüero'" t-raw="0"/>`);
qweb.addTemplate( qweb.addTemplate("_callee-uses-foo", `<span t-esc="foo">foo default</span>`);
"_callee-uses-foo", qweb.addTemplate("_callee-asc-toto", `<div t-raw="toto">toto default</div>`);
`<span t-esc="foo">foo default</span>`
);
qweb.addTemplate(
"_callee-asc-toto",
`<div t-raw="toto">toto default</div>`
);
qweb.addTemplate( qweb.addTemplate(
"caller", "caller",
` `
@@ -847,10 +783,7 @@ describe("t-on", () => {
}); });
test("can bind handlers with object arguments", () => { test("can bind handlers with object arguments", () => {
qweb.addTemplate( qweb.addTemplate("test", `<button t-on-click="add({val: 5})">Click</button>`);
"test",
`<button t-on-click="add({val: 5})">Click</button>`
);
let a = 1; let a = 1;
const node = renderToDOM( const node = renderToDOM(
qweb, qweb,
@@ -868,10 +801,7 @@ describe("t-on", () => {
test("can bind handlers with empty object", () => { test("can bind handlers with empty object", () => {
expect.assertions(2); expect.assertions(2);
qweb.addTemplate( qweb.addTemplate("test", `<button t-on-click="doSomething({})">Click</button>`);
"test",
`<button t-on-click="doSomething({})">Click</button>`
);
const node = renderToDOM( const node = renderToDOM(
qweb, qweb,
"test", "test",
@@ -887,10 +817,7 @@ describe("t-on", () => {
test("can bind handlers with empty object (with non empty inner string)", () => { test("can bind handlers with empty object (with non empty inner string)", () => {
expect.assertions(2); expect.assertions(2);
qweb.addTemplate( qweb.addTemplate("test", `<button t-on-click="doSomething({ })">Click</button>`);
"test",
`<button t-on-click="doSomething({ })">Click</button>`
);
const node = renderToDOM( const node = renderToDOM(
qweb, qweb,
"test", "test",
@@ -1190,13 +1117,10 @@ describe("whitespace handling", () => {
describe("t-key", () => { describe("t-key", () => {
test("can use t-key directive on a node", () => { test("can use t-key directive on a node", () => {
qweb.addTemplate( qweb.addTemplate("test", `<div t-key="beer.id"><t t-esc="beer.name"/></div>`);
"test", expect(renderToString(qweb, "test", { beer: { id: 12, name: "Chimay Rouge" } })).toBe(
`<div t-key="beer.id"><t t-esc="beer.name"/></div>` "<div>Chimay Rouge</div>"
); );
expect(
renderToString(qweb, "test", { beer: { id: 12, name: "Chimay Rouge" } })
).toBe("<div>Chimay Rouge</div>");
}); });
test("t-key directive in a list", () => { test("t-key directive in a list", () => {
+17 -40
View File
@@ -2,9 +2,7 @@ import { compileExpr, tokenize } from "../src/qweb_expressions";
describe("tokenizer", () => { describe("tokenizer", () => {
test("simple tokens", () => { test("simple tokens", () => {
expect(tokenize("1.3")).toEqual([ expect(tokenize("1.3")).toEqual([{ type: "VALUE", value: "1.3" }]);
{ type: "VALUE", value: "1.3" }
]);
expect(tokenize("{}")).toEqual([ expect(tokenize("{}")).toEqual([
{ type: "LEFT_BRACE", value: "{" }, { type: "LEFT_BRACE", value: "{" },
@@ -29,10 +27,7 @@ describe("tokenizer", () => {
{ type: "VALUE", value: "2" }, { type: "VALUE", value: "2" },
{ type: "RIGHT_BRACE", value: "}" } { type: "RIGHT_BRACE", value: "}" }
]); ]);
expect(tokenize("a,")).toEqual([ expect(tokenize("a,")).toEqual([{ type: "SYMBOL", value: "a" }, { type: "COMMA", value: "," }]);
{ type: "SYMBOL", value: "a" },
{ type: "COMMA", value: "," }
]);
expect(tokenize("][")).toEqual([ expect(tokenize("][")).toEqual([
{ type: "RIGHT_BRACKET", value: "]" }, { type: "RIGHT_BRACKET", value: "]" },
{ type: "LEFT_BRACKET", value: "[" } { type: "LEFT_BRACKET", value: "[" }
@@ -46,7 +41,7 @@ describe("tokenizer", () => {
{ type: "OPERATOR", value: "<" }, { type: "OPERATOR", value: "<" },
{ type: "OPERATOR", value: ">" }, { type: "OPERATOR", value: ">" },
{ type: "OPERATOR", value: "!==" }, { type: "OPERATOR", value: "!==" },
{ type: "OPERATOR", value: "!=" }, { type: "OPERATOR", value: "!=" }
]); ]);
}); });
@@ -54,25 +49,17 @@ describe("tokenizer", () => {
expect(() => tokenize("'")).toThrow("Invalid expression"); expect(() => tokenize("'")).toThrow("Invalid expression");
expect(() => tokenize("'\\")).toThrow("Invalid expression"); expect(() => tokenize("'\\")).toThrow("Invalid expression");
expect(() => tokenize("'\\'")).toThrow("Invalid expression"); expect(() => tokenize("'\\'")).toThrow("Invalid expression");
expect(tokenize("'hello ged'")).toEqual([ expect(tokenize("'hello ged'")).toEqual([{ type: "VALUE", value: "'hello ged'" }]);
{ type: "VALUE", value: "'hello ged'" } expect(tokenize("'hello \\'ged\\''")).toEqual([{ type: "VALUE", value: "'hello \\'ged\\''" }]);
]);
expect(tokenize("'hello \\'ged\\''")).toEqual([
{ type: "VALUE", value: "'hello \\'ged\\''" }
]);
expect(() => tokenize('"')).toThrow("Invalid expression"); expect(() => tokenize('"')).toThrow("Invalid expression");
expect(() => tokenize('"\\"')).toThrow("Invalid expression"); expect(() => tokenize('"\\"')).toThrow("Invalid expression");
expect(tokenize('"hello ged"')).toEqual([ expect(tokenize('"hello ged"')).toEqual([{ type: "VALUE", value: '"hello ged"' }]);
{ type: "VALUE", value: '"hello ged"' }
]);
expect(tokenize('"hello ged"}')).toEqual([ expect(tokenize('"hello ged"}')).toEqual([
{ type: "VALUE", value: '"hello ged"' }, { type: "VALUE", value: '"hello ged"' },
{ type: "RIGHT_BRACE", value: "}" } { type: "RIGHT_BRACE", value: "}" }
]); ]);
expect(tokenize('"hello \\"ged\\""')).toEqual([ expect(tokenize('"hello \\"ged\\""')).toEqual([{ type: "VALUE", value: '"hello \\"ged\\""' }]);
{ type: "VALUE", value: '"hello \\"ged\\""' }
]);
}); });
}); });
@@ -113,9 +100,7 @@ describe("expression evaluation", () => {
test("arrays and objects", () => { test("arrays and objects", () => {
expect(compileExpr("[{b:1}] ", {})).toBe("[{b:1}]"); expect(compileExpr("[{b:1}] ", {})).toBe("[{b:1}]");
expect(compileExpr("{a: []} ", {})).toBe("{a:[]}"); expect(compileExpr("{a: []} ", {})).toBe("{a:[]}");
expect(compileExpr("[{b:1, c: [1, {d: {e: 3}} ]}] ", {})).toBe( expect(compileExpr("[{b:1, c: [1, {d: {e: 3}} ]}] ", {})).toBe("[{b:1,c:[1,{d:{e:3}}]}]");
"[{b:1,c:[1,{d:{e:3}}]}]"
);
}); });
test("dot operator", () => { test("dot operator", () => {
@@ -130,13 +115,9 @@ describe("expression evaluation", () => {
}); });
test("various binary operators", () => { test("various binary operators", () => {
expect(compileExpr("color == 'black'", {})).toBe( expect(compileExpr("color == 'black'", {})).toBe("context['color']=='black'");
"context['color']=='black'"
);
expect(compileExpr("a || b", {})).toBe("context['a']||context['b']"); expect(compileExpr("a || b", {})).toBe("context['a']||context['b']");
expect(compileExpr("color === 'black'", {})).toBe( expect(compileExpr("color === 'black'", {})).toBe("context['color']==='black'");
"context['color']==='black'"
);
expect(compileExpr("'li_'+item", {})).toBe("'li_'+context['item']"); expect(compileExpr("'li_'+item", {})).toBe("'li_'+context['item']");
expect(compileExpr("state.val > 1", {})).toBe("context['state'].val>1"); expect(compileExpr("state.val > 1", {})).toBe("context['state'].val>1");
}); });
@@ -164,18 +145,14 @@ describe("expression evaluation", () => {
expect(compileExpr("a()", {})).toBe("context['a']()"); expect(compileExpr("a()", {})).toBe("context['a']()");
expect(compileExpr("a(1)", {})).toBe("context['a'](1)"); expect(compileExpr("a(1)", {})).toBe("context['a'](1)");
expect(compileExpr("a(1,2)", {})).toBe("context['a'](1,2)"); expect(compileExpr("a(1,2)", {})).toBe("context['a'](1,2)");
expect(compileExpr("a(1,2,{a:[a]})", {})).toBe( expect(compileExpr("a(1,2,{a:[a]})", {})).toBe("context['a'](1,2,{a:[context['a']]})");
"context['a'](1,2,{a:[context['a']]})"
);
expect(compileExpr("'x'.toUpperCase()", {})).toBe("'x'.toUpperCase()"); expect(compileExpr("'x'.toUpperCase()", {})).toBe("'x'.toUpperCase()");
expect(compileExpr("'x'.toUpperCase({a: 3})", {})).toBe( expect(compileExpr("'x'.toUpperCase({a: 3})", {})).toBe("'x'.toUpperCase({a:3})");
"'x'.toUpperCase({a:3})" expect(compileExpr("'x'.toUpperCase(a)", { a: { id: "_v5", expr: "" } })).toBe(
"'x'.toUpperCase(_v5)"
);
expect(compileExpr("'x'.toUpperCase({b: a})", { a: { id: "_v5", expr: "" } })).toBe(
"'x'.toUpperCase({b:_v5})"
); );
expect(
compileExpr("'x'.toUpperCase(a)", { a: { id: "_v5", expr: "" } })
).toBe("'x'.toUpperCase(_v5)");
expect(
compileExpr("'x'.toUpperCase({b: a})", { a: { id: "_v5", expr: "" } })
).toBe("'x'.toUpperCase({b:_v5})");
}); });
}); });
+6 -21
View File
@@ -1,11 +1,6 @@
import { Component, Env } from "../src/component"; import { Component, Env } from "../src/component";
import { connect, Store } from "../src/store"; import { connect, Store } from "../src/store";
import { import { makeTestFixture, makeTestEnv, nextMicroTick, nextTick } from "./helpers";
makeTestFixture,
makeTestEnv,
nextMicroTick,
nextTick
} from "./helpers";
import { Observer } from "../src"; import { Observer } from "../src";
describe("basic use", () => { describe("basic use", () => {
@@ -794,9 +789,7 @@ describe("connecting a component to store", () => {
store.commit("addTodo", "hoegaarden"); store.commit("addTodo", "hoegaarden");
await nextTick(); await nextTick();
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe("<div><span>jupiler</span><span>hoegaarden</span></div>");
"<div><span>jupiler</span><span>hoegaarden</span></div>"
);
}); });
test("connect receives store getters as third argument", async () => { test("connect receives store getters as third argument", async () => {
@@ -930,9 +923,7 @@ describe("connecting a component to store", () => {
store.commit("addBeer", "kwak"); store.commit("addBeer", "kwak");
await nextTick(); await nextTick();
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe("<div><span>jupiler</span><span>kwak</span></div>");
"<div><span>jupiler</span><span>kwak</span></div>"
);
}); });
test("connected component with undefined, null and string props", async () => { test("connected component with undefined, null and string props", async () => {
@@ -984,9 +975,7 @@ describe("connecting a component to store", () => {
const app = new App(env); const app = new App(env);
await app.mount(fixture); await app.mount(fixture);
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe("<div><div><span>taster:aaron</span></div></div>");
"<div><div><span>taster:aaron</span></div></div>"
);
app.state.beerId = 1; app.state.beerId = 1;
await nextTick(); await nextTick();
@@ -1062,9 +1051,7 @@ describe("connecting a component to store", () => {
const app = new App(env); const app = new App(env);
await app.mount(fixture); await app.mount(fixture);
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe("<div><div><span>taster:aaron</span></div></div>");
"<div><div><span>taster:aaron</span></div></div>"
);
app.state.beerId = 1; app.state.beerId = 1;
await nextTick(); await nextTick();
@@ -1334,9 +1321,7 @@ describe("connecting a component to store", () => {
await nextTick(); await nextTick();
expect(renderCount).toBe(1); expect(renderCount).toBe(1);
expect(fCount).toBe(1); expect(fCount).toBe(1);
expect(fixture.innerHTML).toBe( expect(fixture.innerHTML).toBe('<div class="todoapp"></div>');
'<div class="todoapp"></div>'
);
}); });
test("connected component willpatch/patch hooks are called on store updates", async () => { test("connected component willpatch/patch hooks are called on store updates", async () => {
+28 -143
View File
@@ -59,9 +59,7 @@ describe("attributes", function() {
test("are set correctly when namespaced", function() { test("are set correctly when namespaced", function() {
const vnode1 = h("div", { attrs: { "xlink:href": "#foo" } }); const vnode1 = h("div", { attrs: { "xlink:href": "#foo" } });
elm = patch(vnode0, vnode1).elm; elm = patch(vnode0, vnode1).elm;
expect(elm.getAttributeNS("http://www.w3.org/1999/xlink", "href")).toBe( expect(elm.getAttributeNS("http://www.w3.org/1999/xlink", "href")).toBe("#foo");
"#foo"
);
}); });
test("should not touch class nor id fields", function() { test("should not touch class nor id fields", function() {
@@ -207,12 +205,8 @@ describe("snabbdom", function() {
expect(elm.firstChild.namespaceURI).toBe(SVGNamespace); expect(elm.firstChild.namespaceURI).toBe(SVGNamespace);
// verify that svg tag automatically gets svg namespace // verify that svg tag automatically gets svg namespace
elm = patch( elm = patch(vnode0, h("svg", [h("foreignObject", [h("div", ["I am HTML embedded in SVG"])])]))
vnode0, .elm;
h("svg", [
h("foreignObject", [h("div", ["I am HTML embedded in SVG"])])
])
).elm;
expect(elm.namespaceURI).toBe(SVGNamespace); expect(elm.namespaceURI).toBe(SVGNamespace);
expect(elm.firstChild.namespaceURI).toBe(SVGNamespace); expect(elm.firstChild.namespaceURI).toBe(SVGNamespace);
expect(elm.firstChild.firstChild.namespaceURI).toBe(XHTMLNamespace); expect(elm.firstChild.firstChild.namespaceURI).toBe(XHTMLNamespace);
@@ -352,13 +346,7 @@ describe("snabbdom", function() {
elm = patch(vnode0, vnode1).elm; elm = patch(vnode0, vnode1).elm;
expect(elm.children.length).toBe(2); expect(elm.children.length).toBe(2);
elm = patch(vnode1, vnode2).elm; elm = patch(vnode1, vnode2).elm;
expect(map(elm.children, c => c.innerHTML)).toEqual([ expect(map(elm.children, c => c.innerHTML)).toEqual(["1", "2", "3", "4", "5"]);
"1",
"2",
"3",
"4",
"5"
]);
}); });
test("add elements in the middle", function() { test("add elements in the middle", function() {
@@ -368,13 +356,7 @@ describe("snabbdom", function() {
expect(elm.children.length).toBe(4); expect(elm.children.length).toBe(4);
expect(elm.children.length).toBe(4); expect(elm.children.length).toBe(4);
elm = patch(vnode1, vnode2).elm; elm = patch(vnode1, vnode2).elm;
expect(map(elm.children, c => c.innerHTML)).toEqual([ expect(map(elm.children, c => c.innerHTML)).toEqual(["1", "2", "3", "4", "5"]);
"1",
"2",
"3",
"4",
"5"
]);
}); });
test("add elements at beginning and end", function() { test("add elements at beginning and end", function() {
@@ -383,13 +365,7 @@ describe("snabbdom", function() {
elm = patch(vnode0, vnode1).elm; elm = patch(vnode0, vnode1).elm;
expect(elm.children.length).toBe(3); expect(elm.children.length).toBe(3);
elm = patch(vnode1, vnode2).elm; elm = patch(vnode1, vnode2).elm;
expect(map(elm.children, c => c.innerHTML)).toEqual([ expect(map(elm.children, c => c.innerHTML)).toEqual(["1", "2", "3", "4", "5"]);
"1",
"2",
"3",
"4",
"5"
]);
}); });
test("adds children to parent with no children", function() { test("adds children to parent with no children", function() {
@@ -588,35 +564,19 @@ describe("snabbdom", function() {
elm = patch(vnode0, vnode1).elm; elm = patch(vnode0, vnode1).elm;
expect(elm.children.length).toBe(6); expect(elm.children.length).toBe(6);
elm = patch(vnode1, vnode2).elm; elm = patch(vnode1, vnode2).elm;
expect(map(elm.children, c => c.innerHTML)).toEqual([ expect(map(elm.children, c => c.innerHTML)).toEqual(["4", "3", "2", "1", "5", "0"]);
"4",
"3",
"2",
"1",
"5",
"0"
]);
}); });
test("supports null/undefined children", function() { test("supports null/undefined children", function() {
const vnode1 = h("i", [0, 1, 2, 3, 4, 5].map(spanNum)); const vnode1 = h("i", [0, 1, 2, 3, 4, 5].map(spanNum));
const vnode2 = h( const vnode2 = h(
"i", "i",
[null, 2, undefined, null, 1, 0, null, 5, 4, null, 3, undefined].map( [null, 2, undefined, null, 1, 0, null, 5, 4, null, 3, undefined].map(spanNum)
spanNum
)
); );
elm = patch(vnode0, vnode1).elm; elm = patch(vnode0, vnode1).elm;
expect(elm.children.length).toBe(6); expect(elm.children.length).toBe(6);
elm = patch(vnode1, vnode2).elm; elm = patch(vnode1, vnode2).elm;
expect(map(elm.children, c => c.innerHTML)).toEqual([ expect(map(elm.children, c => c.innerHTML)).toEqual(["2", "1", "0", "5", "4", "3"]);
"2",
"1",
"0",
"5",
"4",
"3"
]);
}); });
test("supports all null/undefined children", function() { test("supports all null/undefined children", function() {
@@ -627,14 +587,7 @@ describe("snabbdom", function() {
elm = patch(vnode1, vnode2).elm; elm = patch(vnode1, vnode2).elm;
expect(elm.children.length).toBe(0); expect(elm.children.length).toBe(0);
elm = patch(vnode2, vnode3).elm; elm = patch(vnode2, vnode3).elm;
expect(map(elm.children, c => c.innerHTML)).toEqual([ expect(map(elm.children, c => c.innerHTML)).toEqual(["5", "4", "3", "2", "1", "0"]);
"5",
"4",
"3",
"2",
"1",
"0"
]);
}); });
}); });
}); });
@@ -714,18 +667,10 @@ describe("snabbdom", function() {
}); });
test("removes elements", function() { test("removes elements", function() {
const vnode1 = h("div", [ const vnode1 = h("div", [h("span", "One"), h("span", "Two"), h("span", "Three")]);
h("span", "One"),
h("span", "Two"),
h("span", "Three")
]);
const vnode2 = h("div", [h("span", "One"), h("span", "Three")]); const vnode2 = h("div", [h("span", "One"), h("span", "Three")]);
elm = patch(vnode0, vnode1).elm; elm = patch(vnode0, vnode1).elm;
expect(map(elm.children, c => c.innerHTML)).toEqual([ expect(map(elm.children, c => c.innerHTML)).toEqual(["One", "Two", "Three"]);
"One",
"Two",
"Three"
]);
elm = patch(vnode1, vnode2).elm; elm = patch(vnode1, vnode2).elm;
expect(map(elm.children, c => c.innerHTML)).toEqual(["One", "Three"]); expect(map(elm.children, c => c.innerHTML)).toEqual(["One", "Three"]);
}); });
@@ -760,49 +705,19 @@ describe("snabbdom", function() {
}); });
test("reorders elements", function() { test("reorders elements", function() {
const vnode1 = h("div", [ const vnode1 = h("div", [h("span", "One"), h("div", "Two"), h("b", "Three")]);
h("span", "One"), const vnode2 = h("div", [h("b", "Three"), h("span", "One"), h("div", "Two")]);
h("div", "Two"),
h("b", "Three")
]);
const vnode2 = h("div", [
h("b", "Three"),
h("span", "One"),
h("div", "Two")
]);
elm = patch(vnode0, vnode1).elm; elm = patch(vnode0, vnode1).elm;
expect(map(elm.children, c => c.innerHTML)).toEqual([ expect(map(elm.children, c => c.innerHTML)).toEqual(["One", "Two", "Three"]);
"One",
"Two",
"Three"
]);
elm = patch(vnode1, vnode2).elm; elm = patch(vnode1, vnode2).elm;
expect(map(elm.children, c => c.tagName)).toEqual(["B", "SPAN", "DIV"]); expect(map(elm.children, c => c.tagName)).toEqual(["B", "SPAN", "DIV"]);
expect(map(elm.children, c => c.innerHTML)).toEqual([ expect(map(elm.children, c => c.innerHTML)).toEqual(["Three", "One", "Two"]);
"Three",
"One",
"Two"
]);
}); });
test("supports null/undefined children", function() { test("supports null/undefined children", function() {
const vnode1 = h("i", [null, h("i", "1"), h("i", "2"), null]); const vnode1 = h("i", [null, h("i", "1"), h("i", "2"), null]);
const vnode2 = h("i", [ const vnode2 = h("i", [h("i", "2"), undefined, undefined, h("i", "1"), undefined]);
h("i", "2"), const vnode3 = h("i", [null, h("i", "1"), undefined, null, h("i", "2"), undefined, null]);
undefined,
undefined,
h("i", "1"),
undefined
]);
const vnode3 = h("i", [
null,
h("i", "1"),
undefined,
null,
h("i", "2"),
undefined,
null
]);
elm = patch(vnode0, vnode1).elm; elm = patch(vnode0, vnode1).elm;
expect(map(elm.children, c => c.innerHTML)).toEqual(["1", "2"]); expect(map(elm.children, c => c.innerHTML)).toEqual(["1", "2"]);
elm = patch(vnode1, vnode2).elm; elm = patch(vnode1, vnode2).elm;
@@ -835,10 +750,7 @@ describe("snabbdom", function() {
} }
const vnode1 = h("div", [ const vnode1 = h("div", [
h("span", "First sibling"), h("span", "First sibling"),
h("div", { hook: { create: cb } }, [ h("div", { hook: { create: cb } }, [h("span", "Child 1"), h("span", "Child 2")]),
h("span", "Child 1"),
h("span", "Child 2")
]),
h("span", "Can't touch me") h("span", "Can't touch me")
]); ]);
patch(vnode0, vnode1); patch(vnode0, vnode1);
@@ -855,10 +767,7 @@ describe("snabbdom", function() {
} }
const vnode1 = h("div", [ const vnode1 = h("div", [
h("span", "First sibling"), h("span", "First sibling"),
h("div", { hook: { insert: cb } }, [ h("div", { hook: { insert: cb } }, [h("span", "Child 1"), h("span", "Child 2")]),
h("span", "Child 1"),
h("span", "Child 2")
]),
h("span", "Can touch me") h("span", "Can touch me")
]); ]);
patch(vnode0, vnode1); patch(vnode0, vnode1);
@@ -874,17 +783,11 @@ describe("snabbdom", function() {
} }
const vnode1 = h("div", [ const vnode1 = h("div", [
h("span", "First sibling"), h("span", "First sibling"),
h("div", { hook: { prepatch: cb } }, [ h("div", { hook: { prepatch: cb } }, [h("span", "Child 1"), h("span", "Child 2")])
h("span", "Child 1"),
h("span", "Child 2")
])
]); ]);
const vnode2 = h("div", [ const vnode2 = h("div", [
h("span", "First sibling"), h("span", "First sibling"),
h("div", { hook: { prepatch: cb } }, [ h("div", { hook: { prepatch: cb } }, [h("span", "Child 1"), h("span", "Child 2")])
h("span", "Child 1"),
h("span", "Child 2")
])
]); ]);
patch(vnode0, vnode1); patch(vnode0, vnode1);
patch(vnode1, vnode2); patch(vnode1, vnode2);
@@ -965,10 +868,7 @@ describe("snabbdom", function() {
} }
const vnode1 = h("div", [ const vnode1 = h("div", [
h("span", "First sibling"), h("span", "First sibling"),
h("div", { hook: { remove: cb } }, [ h("div", { hook: { remove: cb } }, [h("span", "Child 1"), h("span", "Child 2")])
h("span", "Child 1"),
h("span", "Child 2")
])
]); ]);
const vnode2 = h("div", [h("span", "First sibling")]); const vnode2 = h("div", [h("span", "First sibling")]);
patch(vnode0, vnode1); patch(vnode0, vnode1);
@@ -981,9 +881,7 @@ describe("snabbdom", function() {
function cb(vnode) { function cb(vnode) {
calls++; calls++;
} }
const vnode1 = h("div", [ const vnode1 = h("div", [h("div", { hook: { destroy: cb } }, [h("span", "Child 1")])]);
h("div", { hook: { destroy: cb } }, [h("span", "Child 1")])
]);
const vnode2 = h("div", "Text node"); const vnode2 = h("div", "Text node");
patch(vnode0, vnode1); patch(vnode0, vnode1);
patch(vnode1, vnode2); patch(vnode1, vnode2);
@@ -1053,10 +951,7 @@ describe("snabbdom", function() {
result.push(vnode); result.push(vnode);
rm(); rm();
} }
const vnode1 = h("div", { hook: { remove: cb } }, [ const vnode1 = h("div", { hook: { remove: cb } }, [h("b", "Child 1"), h("i", "Child 2")]);
h("b", "Child 1"),
h("i", "Child 2")
]);
const vnode2 = h("span", [h("b", "Child 1"), h("i", "Child 2")]); const vnode2 = h("span", [h("b", "Child 1"), h("i", "Child 2")]);
patch(vnode0, vnode1); patch(vnode0, vnode1);
patch(vnode1, vnode2); patch(vnode1, vnode2);
@@ -1091,10 +986,7 @@ describe("snabbdom", function() {
} }
const vnode1 = h("div", [ const vnode1 = h("div", [
h("span", "First sibling"), h("span", "First sibling"),
h("div", [ h("div", [h("span", { hook: { destroy: cb } }, "Child 1"), h("span", "Child 2")])
h("span", { hook: { destroy: cb } }, "Child 1"),
h("span", "Child 2")
])
]); ]);
const vnode2 = h("div"); const vnode2 = h("div");
patch(vnode0, vnode1); patch(vnode0, vnode1);
@@ -1150,11 +1042,7 @@ describe("snabbdom", function() {
} }
} }
]); ]);
const vnode1 = h("div", [ const vnode1 = h("div", [h("span", "First child"), "", h("span", "Third child")]);
h("span", "First child"),
"",
h("span", "Third child")
]);
const vnode2 = h("div"); const vnode2 = h("div");
patch(vnode0, vnode1); patch(vnode0, vnode1);
patch(vnode1, vnode2); patch(vnode1, vnode2);
@@ -1196,10 +1084,7 @@ describe("snabbdom", function() {
function cb(vnode) { function cb(vnode) {
result.push(vnode); result.push(vnode);
} }
const vnode1 = h("div", [ const vnode1 = h("div", [h("span", { hook: { update: cb } }, "Hello"), h("span", "there")]);
h("span", { hook: { update: cb } }, "Hello"),
h("span", "there")
]);
patch(vnode0, vnode1); patch(vnode0, vnode1);
patch(vnode1, vnode1); patch(vnode1, vnode1);
expect(result).toHaveLength(0); expect(result).toHaveLength(0);
+2 -11
View File
@@ -1,9 +1,4 @@
import { import { buildData, startMeasure, stopMeasure, formatNumber } from "../shared/utils.js";
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Likes Counter Widget // Likes Counter Widget
@@ -40,11 +35,7 @@ class App extends owl.Component {
state = { messages: [], multipleFlag: false, clearAfterFlag: false }; state = { messages: [], multipleFlag: false, clearAfterFlag: false };
mounted() { mounted() {
this.log( this.log(`Benchmarking Owl v${owl.__info__.version} (build date: ${owl.__info__.date})`);
`Benchmarking Owl v${owl.__info__.version} (build date: ${
owl.__info__.date
})`
);
} }
benchmark(message, fn, callback) { benchmark(message, fn, callback) {
+2 -11
View File
@@ -1,9 +1,4 @@
import { import { buildData, startMeasure, stopMeasure, formatNumber } from "../shared/utils.js";
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Likes Counter Widget // Likes Counter Widget
@@ -40,11 +35,7 @@ class App extends owl.Component {
state = { messages: [], multipleFlag: false, clearAfterFlag: false }; state = { messages: [], multipleFlag: false, clearAfterFlag: false };
mounted() { mounted() {
this.log( this.log(`Benchmarking Owl v${owl.__info__.version} (build date: ${owl.__info__.date})`);
`Benchmarking Owl v${owl.__info__.version} (build date: ${
owl.__info__.date
})`
);
} }
benchmark(message, fn, callback) { benchmark(message, fn, callback) {
+2 -11
View File
@@ -1,9 +1,4 @@
import { import { buildData, startMeasure, stopMeasure, formatNumber } from "../shared/utils.js";
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Likes Counter Widget // Likes Counter Widget
@@ -40,11 +35,7 @@ class App extends owl.Component {
state = { messages: [], multipleFlag: false, clearAfterFlag: false }; state = { messages: [], multipleFlag: false, clearAfterFlag: false };
mounted() { mounted() {
this.log( this.log(`Benchmarking Owl v${owl.__info__.version} (build date: ${owl.__info__.date})`);
`Benchmarking Owl v${owl.__info__.version} (build date: ${
owl.__info__.date
})`
);
} }
benchmark(message, fn, callback) { benchmark(message, fn, callback) {
+3 -14
View File
@@ -1,9 +1,4 @@
import { import { buildData, startMeasure, stopMeasure, formatNumber } from "../shared/utils.js";
buildData,
startMeasure,
stopMeasure,
formatNumber
} from "../shared/utils.js";
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
// Likes Counter Widget // Likes Counter Widget
@@ -40,11 +35,7 @@ class App extends owl.Component {
state = { messages: [], multipleFlag: false, clearAfterFlag: false }; state = { messages: [], multipleFlag: false, clearAfterFlag: false };
mounted() { mounted() {
this.log( this.log(`Benchmarking Owl v${owl.__info__.version} (build date: ${owl.__info__.date})`);
`Benchmarking Owl v${owl.__info__.version} (build date: ${
owl.__info__.date
})`
);
} }
benchmark(message, fn, callback) { benchmark(message, fn, callback) {
@@ -128,9 +119,7 @@ class App extends owl.Component {
removeMessage(event) { removeMessage(event) {
this.benchmark("remove message", () => { this.benchmark("remove message", () => {
const index = this.state.messages.findIndex( const index = this.state.messages.findIndex(m => m.id === event.detail.id);
m => m.id === event.detail.id
);
this.state.messages.splice(index, 1); this.state.messages.splice(index, 1);
}); });
} }
+1 -1
View File
@@ -51,7 +51,7 @@ export function stopMeasure(cb) {
const msg = `[${last}] took ${formatNumber(delta)}ms`; const msg = `[${last}] took ${formatNumber(delta)}ms`;
console.log(msg); console.log(msg);
if (cb) { if (cb) {
cb({msg, delta}); cb({ msg, delta });
} }
}, 0); }, 0);
} }
+1 -4
View File
@@ -97,10 +97,7 @@ function makeCodeIframe(js, css, xml, errorHandler) {
setTimeout(function() { setTimeout(function() {
if (iframe.contentWindow) { if (iframe.contentWindow) {
iframe.contentWindow.removeEventListener("error", errorHandler); iframe.contentWindow.removeEventListener("error", errorHandler);
iframe.contentWindow.removeEventListener( iframe.contentWindow.removeEventListener("unhandledrejection", errorHandler);
"unhandledrejection",
errorHandler
);
} }
}, 200); }, 200);
doc.body.appendChild(script); doc.body.appendChild(script);
+1 -1
View File
@@ -1351,7 +1351,7 @@ export const SAMPLES = [
description: "Lifecycle demo", description: "Lifecycle demo",
code: LIFECYCLE_DEMO, code: LIFECYCLE_DEMO,
xml: LIFECYCLE_DEMO_XML, xml: LIFECYCLE_DEMO_XML,
css: LIFECYCLE_CSS, css: LIFECYCLE_CSS
}, },
{ {
description: "Todo List App (with store)", description: "Todo List App (with store)",