mirror of
https://github.com/odoo/owl.git
synced 2025-10-06 19:59:41 +07:00
[FIX] component: reorganize fiber update algorithm
Closes #473 Closes #484 Closes #489 Closes #492 Closes #512 Co-authored-by: Aaron Bohy <aab@odoo.com>
This commit is contained in:
@@ -781,15 +781,14 @@ component (with some code like `app.mount(document.body)`).
|
||||
1. `willStart` is called on `E`
|
||||
2. template `E` is rendered
|
||||
|
||||
3. component `A` is patched into a detached DOM element. This will create the actual
|
||||
component `A` DOM structure. The patching process will cause recursively the
|
||||
patching of the `B`, `C`, `D` and `E` DOM trees. (so the actual full DOM tree is created
|
||||
3. each components are patched into a detached DOM element, in the following order:
|
||||
`E`, `D`, `C`, `B`, `A`. (so the actual full DOM tree is created
|
||||
in one pass)
|
||||
|
||||
4. the component `A` root element is actually appended to `document.body`
|
||||
|
||||
5. The method `mounted` is called recursively on all components in the following
|
||||
order: `B`, `D`, `E`, `C`, `A`.
|
||||
order: `E`, `D`, `C`, `B`, `A`.
|
||||
|
||||
**Scenario 2: rerendering a component**. Now, let's assume that the user clicked on some
|
||||
button in `C`, and this results in a state update, which is supposed to:
|
||||
@@ -823,14 +822,14 @@ Here is what Owl will do:
|
||||
3. `willPatch` hooks are called recursively on components `C`, `D` (not on `F`,
|
||||
because it is not mounted yet)
|
||||
|
||||
4. component `C` is patched, which will cause recursively:
|
||||
4. components `F`, `D` are patched in that order
|
||||
|
||||
2. `willUnmount` hook on `E`, then destruction of `E`,
|
||||
3. (initial) patching of `F`, then hook `mounted` is called on `F`
|
||||
5. component `C` is patched, which will cause recursively:
|
||||
|
||||
5. patching of `D`
|
||||
1. `willUnmount` hook on `E`
|
||||
2. destruction of `E`,
|
||||
|
||||
6. `patched` hooks are called on `D`, `C`
|
||||
6. `mounted` hook is called on `F`, `patched` hooks are called on `D`, `C`
|
||||
|
||||
### Props Validation
|
||||
|
||||
|
||||
@@ -295,13 +295,12 @@ export class Component<T extends Env, Props extends {}> {
|
||||
return Promise.resolve();
|
||||
}
|
||||
if (!(target instanceof HTMLElement || target instanceof DocumentFragment)) {
|
||||
let message = `Component '${
|
||||
this.constructor.name
|
||||
}' cannot be mounted: the target is not a valid DOM node.`;
|
||||
let message = `Component '${this.constructor.name}' cannot be mounted: the target is not a valid DOM node.`;
|
||||
message += `\nMaybe the DOM is not ready yet? (in that case, you can use owl.utils.whenReady)`;
|
||||
throw new Error(message);
|
||||
}
|
||||
const fiber = new Fiber(null, this, false, target);
|
||||
fiber.shouldPatch = false;
|
||||
if (!__owl__.vnode) {
|
||||
this.__prepareAndRender(fiber);
|
||||
} else {
|
||||
@@ -451,13 +450,7 @@ export class Component<T extends Env, Props extends {}> {
|
||||
|
||||
__callMounted() {
|
||||
const __owl__ = this.__owl__;
|
||||
const children = __owl__.children;
|
||||
for (let id in children) {
|
||||
const comp = children[id];
|
||||
if (!comp.__owl__.isMounted && this.el!.contains(comp.el)) {
|
||||
comp.__callMounted();
|
||||
}
|
||||
}
|
||||
|
||||
__owl__.isMounted = true;
|
||||
__owl__.currentFiber = null;
|
||||
this.mounted();
|
||||
@@ -473,6 +466,10 @@ export class Component<T extends Env, Props extends {}> {
|
||||
}
|
||||
this.willUnmount();
|
||||
__owl__.isMounted = false;
|
||||
if (this.__owl__.currentFiber) {
|
||||
this.__owl__.currentFiber.isCompleted = true;
|
||||
this.__owl__.currentFiber.root.counter = 0;
|
||||
}
|
||||
const children = __owl__.children;
|
||||
for (let id in children) {
|
||||
const comp = children[id];
|
||||
@@ -524,7 +521,7 @@ export class Component<T extends Env, Props extends {}> {
|
||||
* Main patching method. We call the virtual dom patch method here to convert
|
||||
* a virtual dom vnode into some actual dom.
|
||||
*/
|
||||
__patch(vnode) {
|
||||
__patch(vnode: VNode) {
|
||||
const __owl__ = this.__owl__;
|
||||
const target = __owl__.vnode || document.createElement(vnode.sel!);
|
||||
__owl__.vnode = patch(target, vnode);
|
||||
@@ -629,26 +626,6 @@ export class Component<T extends Env, Props extends {}> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only called by qweb t-component directive
|
||||
*/
|
||||
__mount(fiber: Fiber, elm: HTMLElement): VNode {
|
||||
if (fiber !== this.__owl__.currentFiber) {
|
||||
fiber = this.__owl__.currentFiber!; // TODO: check if we can remove fiber arg
|
||||
}
|
||||
const vnode = fiber.vnode!;
|
||||
const __owl__ = this.__owl__;
|
||||
if (__owl__.classObj) {
|
||||
(<any>vnode).data.class = Object.assign((<any>vnode).data.class || {}, __owl__.classObj);
|
||||
}
|
||||
__owl__.vnode = patch(elm, vnode);
|
||||
__owl__.currentFiber = null;
|
||||
if (__owl__.parent!.__owl__.isMounted && !__owl__.isMounted) {
|
||||
this.__callMounted();
|
||||
}
|
||||
return __owl__.vnode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Only called by qweb t-component directive (when t-keepalive is set)
|
||||
*/
|
||||
|
||||
@@ -239,10 +239,6 @@ QWeb.addDirective({
|
||||
ctx.addLine(`const ${refKey} = ${ctx.interpolate(ref)};`);
|
||||
refExpr = `context.__owl__.refs[${refKey}] = w${componentID};`;
|
||||
}
|
||||
let transitionsInsertCode = "";
|
||||
if (transition) {
|
||||
transitionsInsertCode = `utils.transitionInsert(vn, '${transition}');`;
|
||||
}
|
||||
let finalizeComponentCode = `w${componentID}.destroy();`;
|
||||
if (ref) {
|
||||
finalizeComponentCode += `delete context.__owl__.refs[${refKey}];`;
|
||||
@@ -251,6 +247,7 @@ QWeb.addDirective({
|
||||
finalizeComponentCode = `let finalize = () => {
|
||||
${finalizeComponentCode}
|
||||
};
|
||||
delete w${componentID}.__owl__.transitionInserted;
|
||||
utils.transitionRemove(vn, '${transition}', finalize);`;
|
||||
}
|
||||
|
||||
@@ -409,6 +406,12 @@ QWeb.addDirective({
|
||||
`if (!W${componentID}) {throw new Error('Cannot find the definition of component "' + componentKey${componentID} + '"')}`
|
||||
);
|
||||
ctx.addLine(`w${componentID} = new W${componentID}(parent, props${componentID});`);
|
||||
if (transition) {
|
||||
ctx.addLine(`const __patch${componentID} = w${componentID}.__patch;`);
|
||||
ctx.addLine(
|
||||
`w${componentID}.__patch = fiber => {__patch${componentID}.call(w${componentID}, fiber); if(!w${componentID}.__owl__.transitionInserted){w${componentID}.__owl__.transitionInserted = true;utils.transitionInsert(w${componentID}.__owl__.vnode, '${transition}');}};`
|
||||
);
|
||||
}
|
||||
ctx.addLine(`parent.__owl__.cmap[${templateKey}] = w${componentID}.__owl__.id;`);
|
||||
|
||||
if (hasSlots) {
|
||||
@@ -438,8 +441,9 @@ QWeb.addDirective({
|
||||
|
||||
ctx.addLine(`let def${defID} = w${componentID}.__prepare(extra.fiber, ${scopeVars});`);
|
||||
// hack: specify empty remove hook to prevent the node from being removed from the DOM
|
||||
const insertHook = refExpr ? `insert(vn) {${refExpr}},` : "";
|
||||
ctx.addLine(
|
||||
`let pvnode = h('dummy', {key: ${templateKey}, hook: {insert(vn) { let nvn=w${componentID}.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;${refExpr}${transitionsInsertCode}},remove() {},destroy(vn) {${finalizeComponentCode}}}});`
|
||||
`let pvnode = h('dummy', {key: ${templateKey}, hook: {${insertHook}remove() {},destroy(vn) {${finalizeComponentCode}}}});`
|
||||
);
|
||||
ctx.addLine(`const fiber = w${componentID}.__owl__.currentFiber;`);
|
||||
ctx.addLine(
|
||||
|
||||
+44
-35
@@ -120,6 +120,7 @@ export class Fiber {
|
||||
*/
|
||||
_remapFiber(oldFiber: Fiber) {
|
||||
oldFiber.cancel();
|
||||
this.shouldPatch = oldFiber.shouldPatch;
|
||||
if (oldFiber === oldFiber.root) {
|
||||
oldFiber.counter++;
|
||||
}
|
||||
@@ -178,54 +179,62 @@ export class Fiber {
|
||||
* are ready, and the scheduler decides to process it.
|
||||
*/
|
||||
complete() {
|
||||
const component = this.component;
|
||||
if (this.target) {
|
||||
component.__patch(this.vnode);
|
||||
this.target.appendChild(component.el!);
|
||||
if (document.body.contains(this.target)) {
|
||||
component.__callMounted();
|
||||
}
|
||||
} else if (component.__owl__.isMounted && this === this.root) {
|
||||
this.patchComponents();
|
||||
}
|
||||
let component = this.component;
|
||||
this.isCompleted = true;
|
||||
}
|
||||
if (!this.target && !component.__owl__.isMounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute and apply the patch queue of the fiber.
|
||||
* 1) Call 'willPatch' on the component of each patch
|
||||
* 2) Call '__patch' on the component of each patch
|
||||
* 3) Call 'patched' on the component of each patch, in reverse order
|
||||
*/
|
||||
patchComponents() {
|
||||
// build patchQueue
|
||||
const patchQueue: Fiber[] = [];
|
||||
const doWork: (Fiber) => Fiber | null = function(f) {
|
||||
if (f.shouldPatch) {
|
||||
patchQueue.push(f);
|
||||
return f.child;
|
||||
}
|
||||
patchQueue.push(f);
|
||||
return f.child;
|
||||
};
|
||||
this._walk(doWork);
|
||||
let component: Component<any, any> = this.component;
|
||||
const patchLen = patchQueue.length;
|
||||
for (let i = 0; i < patchLen; i++) {
|
||||
component = patchQueue[i].component;
|
||||
if (component.__owl__.willPatchCB) {
|
||||
component.__owl__.willPatchCB();
|
||||
}
|
||||
component.willPatch();
|
||||
}
|
||||
|
||||
// call willPatch hook on each fiber of patchQueue
|
||||
for (let i = 0; i < patchLen; i++) {
|
||||
const fiber = patchQueue[i];
|
||||
if (fiber.shouldPatch) {
|
||||
component = fiber.component;
|
||||
if (component.__owl__.willPatchCB) {
|
||||
component.__owl__.willPatchCB();
|
||||
}
|
||||
component.willPatch();
|
||||
}
|
||||
}
|
||||
|
||||
// call __patch on each fiber of (reversed) patchQueue
|
||||
for (let i = patchLen - 1; i >= 0; i--) {
|
||||
const fiber = patchQueue[i];
|
||||
component = fiber.component;
|
||||
component.__patch(fiber.vnode);
|
||||
component.__patch(fiber.vnode!);
|
||||
if (!fiber.shouldPatch && (!fiber.target || i !== 0)) {
|
||||
component.__owl__.pvnode!.elm = component.__owl__.vnode!.elm;
|
||||
}
|
||||
component.__owl__.currentFiber = null;
|
||||
}
|
||||
|
||||
// insert into the DOM (mount case)
|
||||
let inDOM = false;
|
||||
if (this.target) {
|
||||
this.target.appendChild(this.component.el!);
|
||||
inDOM = document.body.contains(this.target);
|
||||
}
|
||||
|
||||
// call patched/mounted hook on each fiber of (reversed) patchQueue
|
||||
for (let i = patchLen - 1; i >= 0; i--) {
|
||||
component = patchQueue[i].component;
|
||||
component.patched();
|
||||
if (component.__owl__.patchedCB) {
|
||||
component.__owl__.patchedCB();
|
||||
const fiber = patchQueue[i];
|
||||
component = fiber.component;
|
||||
if (fiber.shouldPatch && !this.target) {
|
||||
component.patched();
|
||||
if (component.__owl__.patchedCB) {
|
||||
component.__owl__.patchedCB();
|
||||
}
|
||||
} else if (this.target ? inDOM : true) {
|
||||
component.__callMounted();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@ export class Scheduler {
|
||||
}
|
||||
|
||||
addFiber(fiber): Promise<void> {
|
||||
// if the fiber was remapped into a larger rendering fiber, it may not be a
|
||||
// root fiber. But we only want to register root fibers
|
||||
fiber = fiber.root;
|
||||
return new Promise((resolve, reject) => {
|
||||
if (fiber.error) {
|
||||
return reject(fiber.error);
|
||||
|
||||
@@ -131,7 +131,7 @@ export class CompilationContext {
|
||||
subContext(key: keyof CompilationContext, value: any): CompilationContext {
|
||||
const newContext = Object.create(this);
|
||||
newContext[key] = value;
|
||||
if (key === 'caller') {
|
||||
if (key === "caller") {
|
||||
newContext.callingLevel++;
|
||||
newContext.inliningLevel++;
|
||||
}
|
||||
@@ -191,7 +191,7 @@ export class CompilationContext {
|
||||
* in which we are for inlining archs (t-raw="0")
|
||||
*/
|
||||
getInliningContext(): CompilationContext {
|
||||
return this.subContext('inliningLevel', this.inliningLevel - 1);
|
||||
return this.subContext("inliningLevel", this.inliningLevel - 1);
|
||||
}
|
||||
|
||||
getValue(val: any): QWebVar | string {
|
||||
|
||||
@@ -105,6 +105,8 @@ QWeb.utils.transitionInsert = function(vn: VNode, name: string) {
|
||||
|
||||
elm.classList.add(name + "-enter");
|
||||
elm.classList.add(name + "-enter-active");
|
||||
elm.classList.remove(name + "-leave-active");
|
||||
elm.classList.remove(name + "-leave-to");
|
||||
const finalize = () => {
|
||||
elm.classList.remove(name + "-enter-active");
|
||||
elm.classList.remove(name + "-enter-to");
|
||||
@@ -123,6 +125,9 @@ QWeb.utils.transitionRemove = function(vn: VNode, name: string, rm: () => void)
|
||||
elm.classList.add(name + "-leave");
|
||||
elm.classList.add(name + "-leave-active");
|
||||
const finalize = () => {
|
||||
if (!elm.classList.contains(name + "-leave-active")) {
|
||||
return;
|
||||
}
|
||||
elm.classList.remove(name + "-leave-active");
|
||||
elm.classList.remove(name + "-leave-to");
|
||||
rm();
|
||||
|
||||
+6
-4
@@ -176,10 +176,12 @@ export function init(modules: Array<Partial<Module>>, domApi?: DOMAPI) {
|
||||
}
|
||||
vnode.elm = api.createComment(vnode.text as string);
|
||||
} else if (sel !== undefined) {
|
||||
const elm = (vnode.elm =
|
||||
isDef(data) && isDef((i = (data as VNodeData).ns))
|
||||
? api.createElementNS(i, sel)
|
||||
: api.createElement(sel));
|
||||
const elm =
|
||||
vnode.elm ||
|
||||
(vnode.elm =
|
||||
isDef(data) && isDef((i = (data as VNodeData).ns))
|
||||
? api.createElementNS(i, sel)
|
||||
: api.createElement(sel));
|
||||
for (i = 0, iLen = cbs.create.length; i < iLen; ++i) cbs.create[i](emptyNode, vnode);
|
||||
if (array(children)) {
|
||||
for (i = 0, iLen = children.length; i < iLen; ++i) {
|
||||
|
||||
@@ -27,11 +27,14 @@ exports[`animations t-transition combined with component 1`] = `
|
||||
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['Child'];
|
||||
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
|
||||
w3 = new W3(parent, props3);
|
||||
const __patch3 = w3.__patch;
|
||||
w3.__patch = fiber => {__patch3.call(w3, fiber); if(!w3.__owl__.transitionInserted){w3.__owl__.transitionInserted = true;utils.transitionInsert(w3.__owl__.vnode, 'chimay');}};
|
||||
parent.__owl__.cmap[k4] = w3.__owl__.id;
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => {
|
||||
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {let finalize = () => {
|
||||
w3.destroy();
|
||||
};
|
||||
delete w3.__owl__.transitionInserted;
|
||||
utils.transitionRemove(vn, 'chimay', finalize);}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
@@ -71,11 +74,62 @@ exports[`animations t-transition combined with t-component and t-if 1`] = `
|
||||
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['Child'];
|
||||
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
|
||||
w3 = new W3(parent, props3);
|
||||
const __patch3 = w3.__patch;
|
||||
w3.__patch = fiber => {__patch3.call(w3, fiber); if(!w3.__owl__.transitionInserted){w3.__owl__.transitionInserted = true;utils.transitionInsert(w3.__owl__.vnode, 'chimay');}};
|
||||
parent.__owl__.cmap[k4] = w3.__owl__.id;
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;utils.transitionInsert(vn, 'chimay');},remove() {},destroy(vn) {let finalize = () => {
|
||||
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {let finalize = () => {
|
||||
w3.destroy();
|
||||
};
|
||||
delete w3.__owl__.transitionInserted;
|
||||
utils.transitionRemove(vn, 'chimay', finalize);}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
c1.push(pvnode);
|
||||
w3.__owl__.pvnode = pvnode;
|
||||
}
|
||||
w3.__owl__.parentLastFiberId = extra.fiber.id;
|
||||
}
|
||||
return vn1;
|
||||
}"
|
||||
`;
|
||||
|
||||
exports[`animations t-transition combined with t-component, remove and re-add before transitionend 1`] = `
|
||||
"function anonymous(context,extra
|
||||
) {
|
||||
let utils = this.constructor.utils;
|
||||
let QWeb = this.constructor;
|
||||
let parent = context;
|
||||
let owner = context;
|
||||
var h = this.h;
|
||||
let c1 = [], p1 = {key:1};
|
||||
var vn1 = h('div', p1, c1);
|
||||
if (context['state'].flag) {
|
||||
//COMPONENT
|
||||
let k4 = \`__5__\`;
|
||||
let w3 = k4 in parent.__owl__.cmap ? parent.__owl__.children[parent.__owl__.cmap[k4]] : false;
|
||||
let props3 = {};
|
||||
if (w3 && w3.__owl__.currentFiber && !w3.__owl__.vnode) {
|
||||
w3.destroy();
|
||||
w3 = false;
|
||||
}
|
||||
if (w3) {
|
||||
w3.__updateProps(props3, extra.fiber, undefined, undefined);
|
||||
let pvnode = w3.__owl__.pvnode;
|
||||
c1.push(pvnode);
|
||||
} else {
|
||||
let componentKey3 = \`Child\`;
|
||||
let W3 = context.constructor.components[componentKey3] || QWeb.components[componentKey3]|| context['Child'];
|
||||
if (!W3) {throw new Error('Cannot find the definition of component \\"' + componentKey3 + '\\"')}
|
||||
w3 = new W3(parent, props3);
|
||||
const __patch3 = w3.__patch;
|
||||
w3.__patch = fiber => {__patch3.call(w3, fiber); if(!w3.__owl__.transitionInserted){w3.__owl__.transitionInserted = true;utils.transitionInsert(w3.__owl__.vnode, 'chimay');}};
|
||||
parent.__owl__.cmap[k4] = w3.__owl__.id;
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {let finalize = () => {
|
||||
w3.destroy();
|
||||
};
|
||||
delete w3.__owl__.transitionInserted;
|
||||
utils.transitionRemove(vn, 'chimay', finalize);}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
|
||||
+72
-23
@@ -1,13 +1,15 @@
|
||||
import { Component, Env } from "../src/component/component";
|
||||
import { QWeb } from "../src/qweb/index";
|
||||
import { useState, useRef } from "../src/hooks";
|
||||
import { xml } from "../src/tags";
|
||||
import {
|
||||
makeDeferred,
|
||||
makeTestFixture,
|
||||
makeTestEnv,
|
||||
patchNextFrame,
|
||||
renderToDOM,
|
||||
unpatchNextFrame
|
||||
unpatchNextFrame,
|
||||
nextTick
|
||||
} from "./helpers";
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -321,30 +323,23 @@ describe("animations", () => {
|
||||
});
|
||||
|
||||
test("t-transition combined with t-component, remove and re-add before transitionend", async () => {
|
||||
expect.assertions(11);
|
||||
expect.assertions(12);
|
||||
|
||||
env.qweb.addTemplates(
|
||||
`<templates>
|
||||
<div t-name="Parent">
|
||||
<button t-on-click="toggle">Toggle</button>
|
||||
<t t-if="state.flag" t-component="Child" t-transition="chimay"/>
|
||||
</div>
|
||||
<span t-name="Child">blue</span>
|
||||
</templates>`
|
||||
);
|
||||
class Child extends Widget {}
|
||||
class Child extends Widget {
|
||||
static template = xml`<span>blue</span>`;
|
||||
}
|
||||
class Parent extends Widget {
|
||||
static template = xml`
|
||||
<div t-name="Parent">
|
||||
<t t-if="state.flag" t-component="Child" t-transition="chimay"/>
|
||||
</div>`;
|
||||
static components = { Child };
|
||||
state = useState({ flag: false });
|
||||
|
||||
toggle() {
|
||||
this.state.flag = !this.state.flag;
|
||||
}
|
||||
}
|
||||
|
||||
const widget = new Parent();
|
||||
await widget.mount(fixture);
|
||||
let button = widget.el!.querySelector("button");
|
||||
expect(env.qweb.templates[Parent.template].fn.toString()).toMatchSnapshot();
|
||||
|
||||
let def = makeDeferred();
|
||||
let phase = "enter";
|
||||
@@ -357,24 +352,78 @@ describe("animations", () => {
|
||||
def.resolve();
|
||||
});
|
||||
|
||||
// click display the span
|
||||
button!.click();
|
||||
// display the span
|
||||
widget.state.flag = true;
|
||||
await def; // wait for the mocked repaint to be done
|
||||
widget.el!.querySelector("span")!.dispatchEvent(new Event("transitionend")); // mock end of css transition
|
||||
expect(fixture.innerHTML).toBe('<div><button>Toggle</button><span class="">blue</span></div>');
|
||||
expect(fixture.innerHTML).toBe('<div><span class="">blue</span></div>');
|
||||
|
||||
// click to remove the span, and click again to re-add it before transitionend
|
||||
def = makeDeferred();
|
||||
phase = "leave";
|
||||
button!.click();
|
||||
|
||||
widget.state.flag = false;
|
||||
|
||||
await def; // wait for the mocked repaint to be done
|
||||
def = makeDeferred();
|
||||
phase = "enter";
|
||||
button!.click();
|
||||
widget.state.flag = true;
|
||||
|
||||
await def; // wait for the mocked repaint to be done
|
||||
widget.el!.querySelector("span")!.dispatchEvent(new Event("transitionend")); // mock end of css transition
|
||||
expect(fixture.innerHTML).toBe('<div><button>Toggle</button><span class="">blue</span></div>');
|
||||
expect(fixture.innerHTML).toBe('<div><span class="" data-owl-key="__5__">blue</span></div>');
|
||||
});
|
||||
|
||||
test("transitionInsert is called the correct amount of times", async () => {
|
||||
const oldTransitionInsert = QWeb.utils.transitionInsert;
|
||||
QWeb.utils.transitionInsert = jest.fn(oldTransitionInsert);
|
||||
|
||||
class Child extends Widget {
|
||||
static template = xml`<span>blue</span>`;
|
||||
}
|
||||
class Parent extends Widget {
|
||||
static template = xml`
|
||||
<div t-name="Parent">
|
||||
<Child t-if="state.flag" t-transition="chimay"/>
|
||||
</div>`;
|
||||
static components = { Child };
|
||||
state = useState({ flag: false });
|
||||
}
|
||||
|
||||
patchNextFrame(cb => cb());
|
||||
|
||||
const widget = new Parent();
|
||||
await widget.mount(fixture);
|
||||
|
||||
widget.state.flag = true;
|
||||
|
||||
await nextTick();
|
||||
widget.el!.querySelector("span")!.dispatchEvent(new Event("transitionend"));
|
||||
expect(fixture.innerHTML).toBe('<div><span class="">blue</span></div>');
|
||||
expect(QWeb.utils.transitionInsert).toBeCalledTimes(1);
|
||||
|
||||
widget.state.flag = false;
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe(
|
||||
'<div><span class="chimay-leave-active chimay-leave-to" data-owl-key="__5__">blue</span></div>'
|
||||
);
|
||||
expect(QWeb.utils.transitionInsert).toBeCalledTimes(1);
|
||||
|
||||
widget.state.flag = true;
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe(
|
||||
'<div><span class="chimay-enter-active chimay-enter-to" data-owl-key="__5__">blue</span></div>'
|
||||
);
|
||||
expect(QWeb.utils.transitionInsert).toBeCalledTimes(2);
|
||||
|
||||
widget.state.flag = false;
|
||||
await nextTick();
|
||||
widget.state.flag = true;
|
||||
await nextTick();
|
||||
|
||||
expect(QWeb.utils.transitionInsert).toBeCalledTimes(3);
|
||||
widget.el!.querySelector("span")!.dispatchEvent(new Event("transitionend"));
|
||||
expect(fixture.innerHTML).toBe('<div><span class="" data-owl-key="__5__">blue</span></div>');
|
||||
QWeb.utils.transitionInsert = oldTransitionInsert;
|
||||
});
|
||||
});
|
||||
|
||||
@@ -58,7 +58,7 @@ exports[`basic widget properties reconciliation alg works for t-foreach in t-for
|
||||
w9 = new W9(parent, props9);
|
||||
parent.__owl__.cmap[k10] = w9.__owl__.id;
|
||||
let def8 = w9.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k10, hook: {insert(vn) { let nvn=w9.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w9.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k10, hook: {remove() {},destroy(vn) {w9.destroy();}}});
|
||||
const fiber = w9.__owl__.currentFiber;
|
||||
def8.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
c1.push(pvnode);
|
||||
@@ -102,7 +102,7 @@ exports[`basic widget properties t-key on a component with t-if, and a sibling c
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[k5] = w4.__owl__.id;
|
||||
let def3 = w4.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k5, hook: {insert(vn) { let nvn=w4.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k5, hook: {remove() {},destroy(vn) {w4.destroy();}}});
|
||||
const fiber = w4.__owl__.currentFiber;
|
||||
def3.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
c1.push(pvnode);
|
||||
@@ -129,7 +129,7 @@ exports[`basic widget properties t-key on a component with t-if, and a sibling c
|
||||
w8 = new W8(parent, props8);
|
||||
parent.__owl__.cmap[k9] = w8.__owl__.id;
|
||||
let def7 = w8.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k9, hook: {insert(vn) { let nvn=w8.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w8.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k9, hook: {remove() {},destroy(vn) {w8.destroy();}}});
|
||||
const fiber = w8.__owl__.currentFiber;
|
||||
def7.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
c1.push(pvnode);
|
||||
@@ -170,7 +170,7 @@ exports[`class and style attributes with t-component dynamic t-att-style is prop
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap[k4] = w3.__owl__.id;
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.style = _6;}};});
|
||||
c1.push(pvnode);
|
||||
@@ -214,7 +214,7 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap[k4] = w3.__owl__.id;
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;context.__owl__.refs[ref6] = w3;},remove() {},destroy(vn) {w3.destroy();delete context.__owl__.refs[ref6];}}});
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) {context.__owl__.refs[ref6] = w3;},remove() {},destroy(vn) {w3.destroy();delete context.__owl__.refs[ref6];}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){}};});
|
||||
c1.push(pvnode);
|
||||
@@ -272,7 +272,7 @@ exports[`class and style attributes with t-component t-att-class is properly add
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap[k4] = w3.__owl__.id;
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;context.__owl__.refs[ref6] = w3;},remove() {},destroy(vn) {w3.destroy();delete context.__owl__.refs[ref6];}}});
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) {context.__owl__.refs[ref6] = w3;},remove() {},destroy(vn) {w3.destroy();delete context.__owl__.refs[ref6];}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){}};});
|
||||
c1.push(pvnode);
|
||||
@@ -342,7 +342,7 @@ exports[`composition sub components with some state rendered in a loop 1`] = `
|
||||
w7 = new W7(parent, props7);
|
||||
parent.__owl__.cmap[k8] = w7.__owl__.id;
|
||||
let def6 = w7.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k8, hook: {insert(vn) { let nvn=w7.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w7.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k8, hook: {remove() {},destroy(vn) {w7.destroy();}}});
|
||||
const fiber = w7.__owl__.currentFiber;
|
||||
def6.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
c1.push(pvnode);
|
||||
@@ -383,7 +383,7 @@ exports[`composition t-component with dynamic value 1`] = `
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap[k4] = w3.__owl__.id;
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
c1.push(pvnode);
|
||||
@@ -423,7 +423,7 @@ exports[`composition t-component with dynamic value 2 1`] = `
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap[k4] = w3.__owl__.id;
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
c1.push(pvnode);
|
||||
@@ -463,7 +463,7 @@ exports[`dynamic t-props basic use 1`] = `
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap[k4] = w3.__owl__.id;
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
c1.push(pvnode);
|
||||
@@ -507,7 +507,7 @@ exports[`other directives with t-component t-on with getter as handler 1`] = `
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[k5] = w4.__owl__.id;
|
||||
let def3 = w4.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k5, hook: {insert(vn) { let nvn=w4.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k5, hook: {remove() {},destroy(vn) {w4.destroy();}}});
|
||||
const fiber = w4.__owl__.currentFiber;
|
||||
def3.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['handler'];if (fn) { fn.call(owner, e); } else { owner.handler; }});}};});
|
||||
c1.push(pvnode);
|
||||
@@ -547,7 +547,7 @@ exports[`other directives with t-component t-on with handler bound to argument 1
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap[k4] = w3.__owl__.id;
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, 3, e); } else { owner.onEv; }});}};});
|
||||
c1.push(pvnode);
|
||||
@@ -587,7 +587,7 @@ exports[`other directives with t-component t-on with handler bound to empty obje
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap[k4] = w3.__owl__.id;
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, {}, e); } else { owner.onEv; }});}};});
|
||||
c1.push(pvnode);
|
||||
@@ -627,7 +627,7 @@ exports[`other directives with t-component t-on with handler bound to empty obje
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap[k4] = w3.__owl__.id;
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, {}, e); } else { owner.onEv; }});}};});
|
||||
c1.push(pvnode);
|
||||
@@ -667,7 +667,7 @@ exports[`other directives with t-component t-on with handler bound to object 1`]
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap[k4] = w3.__owl__.id;
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, {val:3}, e); } else { owner.onEv; }});}};});
|
||||
c1.push(pvnode);
|
||||
@@ -711,7 +711,7 @@ exports[`other directives with t-component t-on with inline statement 1`] = `
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[k5] = w4.__owl__.id;
|
||||
let def3 = w4.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k5, hook: {insert(vn) { let nvn=w4.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k5, hook: {remove() {},destroy(vn) {w4.destroy();}}});
|
||||
const fiber = w4.__owl__.currentFiber;
|
||||
def3.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['state.counter++'];if (fn) { fn.call(owner, e); } else { owner.state.counter++; }});}};});
|
||||
c1.push(pvnode);
|
||||
@@ -751,7 +751,7 @@ exports[`other directives with t-component t-on with no handler (only modifiers)
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap[k4] = w3.__owl__.id;
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, e); } else { owner.onEv; }});}};});
|
||||
c1.push(pvnode);
|
||||
@@ -791,7 +791,7 @@ exports[`other directives with t-component t-on with prevent and self modifiers
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap[k4] = w3.__owl__.id;
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {e.preventDefault();if (e.target !== vn.elm) {return}const fn = owner['onEv'];if (fn) { fn.call(owner, e); } else { owner.onEv; }});}};});
|
||||
c1.push(pvnode);
|
||||
@@ -831,7 +831,7 @@ exports[`other directives with t-component t-on with self and prevent modifiers
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap[k4] = w3.__owl__.id;
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {if (e.target !== vn.elm) {return}e.preventDefault();const fn = owner['onEv'];if (fn) { fn.call(owner, e); } else { owner.onEv; }});}};});
|
||||
c1.push(pvnode);
|
||||
@@ -871,7 +871,7 @@ exports[`other directives with t-component t-on with self modifier 1`] = `
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap[k4] = w3.__owl__.id;
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev-1', function (e) {const fn = owner['onEv1'];if (fn) { fn.call(owner, e); } else { owner.onEv1; }});vn.elm.addEventListener('ev-2', function (e) {if (e.target !== vn.elm) {return}const fn = owner['onEv2'];if (fn) { fn.call(owner, e); } else { owner.onEv2; }});}};});
|
||||
c1.push(pvnode);
|
||||
@@ -911,7 +911,7 @@ exports[`other directives with t-component t-on with stop and/or prevent modifie
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap[k4] = w3.__owl__.id;
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev-1', function (e) {e.stopPropagation();const fn = owner['onEv1'];if (fn) { fn.call(owner, e); } else { owner.onEv1; }});vn.elm.addEventListener('ev-2', function (e) {e.preventDefault();const fn = owner['onEv2'];if (fn) { fn.call(owner, e); } else { owner.onEv2; }});vn.elm.addEventListener('ev-3', function (e) {e.stopPropagation();e.preventDefault();const fn = owner['onEv3'];if (fn) { fn.call(owner, e); } else { owner.onEv3; }});}};});
|
||||
c1.push(pvnode);
|
||||
@@ -969,7 +969,7 @@ exports[`random stuff/miscellaneous snapshotting compiled code 1`] = `
|
||||
w4 = new W4(parent, props4);
|
||||
parent.__owl__.cmap[k5] = w4.__owl__.id;
|
||||
let def3 = w4.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k5, hook: {insert(vn) { let nvn=w4.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k5, hook: {remove() {},destroy(vn) {w4.destroy();}}});
|
||||
const fiber = w4.__owl__.currentFiber;
|
||||
def3.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
c1.push(pvnode);
|
||||
@@ -1026,7 +1026,7 @@ exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument
|
||||
w7 = new W7(parent, props7);
|
||||
parent.__owl__.cmap[k8] = w7.__owl__.id;
|
||||
let def6 = w7.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k8, hook: {insert(vn) { let nvn=w7.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w7.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k8, hook: {remove() {},destroy(vn) {w7.destroy();}}});
|
||||
const fiber = w7.__owl__.currentFiber;
|
||||
def6.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.addEventListener('ev', function (e) {const fn = owner['onEv'];if (fn) { fn.call(owner, arg10, e); } else { owner.onEv; }});}};});
|
||||
c1.push(pvnode);
|
||||
@@ -1318,7 +1318,7 @@ exports[`t-slot directive can define and call slots 1`] = `
|
||||
parent.__owl__.cmap[k4] = w3.__owl__.id;
|
||||
w3.__owl__.slotId = 1;
|
||||
let def2 = w3.__prepare(extra.fiber, {}, undefined);
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
c1.push(pvnode);
|
||||
@@ -1553,7 +1553,7 @@ exports[`t-slot directive slots are rendered with proper context, part 2 2`] = `
|
||||
parent.__owl__.cmap[k10] = w9.__owl__.id;
|
||||
w9.__owl__.slotId = 1;
|
||||
let def8 = w9.__prepare(extra.fiber, Object.assign({}, scope), undefined);
|
||||
let pvnode = h('dummy', {key: k10, hook: {insert(vn) { let nvn=w9.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w9.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k10, hook: {remove() {},destroy(vn) {w9.destroy();}}});
|
||||
const fiber = w9.__owl__.currentFiber;
|
||||
def8.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
c7.push(pvnode);
|
||||
@@ -1654,7 +1654,7 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
|
||||
parent.__owl__.cmap[k11] = w10.__owl__.id;
|
||||
w10.__owl__.slotId = 1;
|
||||
let def9 = w10.__prepare(extra.fiber, Object.assign({}, scope), {_8});
|
||||
let pvnode = h('dummy', {key: k11, hook: {insert(vn) { let nvn=w10.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w10.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k11, hook: {remove() {},destroy(vn) {w10.destroy();}}});
|
||||
const fiber = w10.__owl__.currentFiber;
|
||||
def9.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
c7.push(pvnode);
|
||||
@@ -1710,7 +1710,7 @@ exports[`t-slot directive slots are rendered with proper context, part 4 1`] = `
|
||||
parent.__owl__.cmap[k5] = w4.__owl__.id;
|
||||
w4.__owl__.slotId = 1;
|
||||
let def3 = w4.__prepare(extra.fiber, {}, {_2});
|
||||
let pvnode = h('dummy', {key: k5, hook: {insert(vn) { let nvn=w4.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k5, hook: {remove() {},destroy(vn) {w4.destroy();}}});
|
||||
const fiber = w4.__owl__.currentFiber;
|
||||
def3.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
c1.push(pvnode);
|
||||
@@ -1782,7 +1782,7 @@ exports[`top level sub widgets basic use 1`] = `
|
||||
w2 = new W2(parent, props2);
|
||||
parent.__owl__.cmap[k3] = w2.__owl__.id;
|
||||
let def1 = w2.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k3, hook: {insert(vn) { let nvn=w2.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w2.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}});
|
||||
const fiber = w2.__owl__.currentFiber;
|
||||
def1.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
utils.defineProxy(vn5, pvnode);
|
||||
@@ -1824,7 +1824,7 @@ exports[`top level sub widgets can select a sub widget 1`] = `
|
||||
w2 = new W2(parent, props2);
|
||||
parent.__owl__.cmap[k3] = w2.__owl__.id;
|
||||
let def1 = w2.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k3, hook: {insert(vn) { let nvn=w2.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w2.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k3, hook: {remove() {},destroy(vn) {w2.destroy();}}});
|
||||
const fiber = w2.__owl__.currentFiber;
|
||||
def1.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
utils.defineProxy(vn5, pvnode);
|
||||
@@ -1854,7 +1854,7 @@ exports[`top level sub widgets can select a sub widget 1`] = `
|
||||
w7 = new W7(parent, props7);
|
||||
parent.__owl__.cmap[k8] = w7.__owl__.id;
|
||||
let def6 = w7.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k8, hook: {insert(vn) { let nvn=w7.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w7.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k8, hook: {remove() {},destroy(vn) {w7.destroy();}}});
|
||||
const fiber = w7.__owl__.currentFiber;
|
||||
def6.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
utils.defineProxy(vn10, pvnode);
|
||||
|
||||
@@ -29,7 +29,7 @@ exports[`props validation props are validated in dev mode (code snapshot) 1`] =
|
||||
w3 = new W3(parent, props3);
|
||||
parent.__owl__.cmap[k4] = w3.__owl__.id;
|
||||
let def2 = w3.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k4, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k4, hook: {remove() {},destroy(vn) {w3.destroy();}}});
|
||||
const fiber = w3.__owl__.currentFiber;
|
||||
def2.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
c1.push(pvnode);
|
||||
|
||||
@@ -1513,6 +1513,25 @@ describe("composition", () => {
|
||||
"<div><span>11</span><span>2</span><span>13</span></div>"
|
||||
);
|
||||
});
|
||||
|
||||
test("three level of components with collapsing root nodes", async () => {
|
||||
class GrandChild extends Component<any, any> {
|
||||
static template = xml`<div>2</div>`;
|
||||
}
|
||||
class Child extends Component<any, any> {
|
||||
static components = { GrandChild };
|
||||
static template = xml`<GrandChild/>`;
|
||||
}
|
||||
class Parent extends Component<any, any> {
|
||||
static components = { Child };
|
||||
static template = xml`<Child></Child>`;
|
||||
}
|
||||
|
||||
const app = new Parent();
|
||||
await app.mount(fixture);
|
||||
|
||||
expect(fixture.innerHTML).toBe("<div>2</div>");
|
||||
});
|
||||
});
|
||||
|
||||
describe("props evaluation ", () => {
|
||||
@@ -2136,6 +2155,40 @@ describe("other directives with t-component", () => {
|
||||
expect(env.qweb.templates[Parent.template].fn.toString()).toMatchSnapshot();
|
||||
});
|
||||
|
||||
test("t-on on nested components with collapsing root nodes", async () => {
|
||||
const steps: string[] = [];
|
||||
let grandChild;
|
||||
class GrandChild extends Component<any, any> {
|
||||
static template = xml`<span t-on-ev="_onEv"/>`;
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
grandChild = this;
|
||||
}
|
||||
_onEv() {
|
||||
steps.push('GrandChild');
|
||||
}
|
||||
}
|
||||
class Child extends Component<any, any> {
|
||||
static template = xml`<GrandChild t-on-ev="_onEv"/>`;
|
||||
static components = { GrandChild };
|
||||
_onEv() {
|
||||
steps.push('Child');
|
||||
}
|
||||
}
|
||||
class Parent extends Component<any, any> {
|
||||
static template = xml`<Child t-on-ev="_onEv"/>`;
|
||||
static components = { Child };
|
||||
_onEv() {
|
||||
steps.push('Parent');
|
||||
}
|
||||
}
|
||||
const parent = new Parent();
|
||||
await parent.mount(fixture);
|
||||
|
||||
grandChild.trigger("ev");
|
||||
expect(steps).toEqual(['GrandChild', 'Child', 'Parent']);
|
||||
});
|
||||
|
||||
test("t-if works with t-component", async () => {
|
||||
env.qweb.addTemplate("ParentWidget", `<div><t t-component="child" t-if="state.flag"/></div>`);
|
||||
class Child extends Widget {}
|
||||
@@ -2244,9 +2297,9 @@ describe("other directives with t-component", () => {
|
||||
<t t-esc="state.val"/>
|
||||
<t t-esc="props.val"/>
|
||||
</span>`;
|
||||
state = useState({ val: 'A' });
|
||||
state = useState({ val: "A" });
|
||||
mounted() {
|
||||
this.state.val = 'B';
|
||||
this.state.val = "B";
|
||||
}
|
||||
}
|
||||
class ParentWidget extends Widget {
|
||||
@@ -2366,10 +2419,6 @@ describe("random stuff/miscellaneous", () => {
|
||||
steps.push(`${this.name}:__patch`);
|
||||
super.__patch(vnode);
|
||||
}
|
||||
__mount(vnode, elm) {
|
||||
steps.push(`${this.name}:__patch(from __mount)`);
|
||||
return super.__mount(vnode, elm);
|
||||
}
|
||||
mounted() {
|
||||
steps.push(`${this.name}:mounted`);
|
||||
}
|
||||
@@ -2466,15 +2515,15 @@ describe("random stuff/miscellaneous", () => {
|
||||
"E:willStart",
|
||||
"D:render",
|
||||
"E:render",
|
||||
"E:__patch",
|
||||
"D:__patch",
|
||||
"C:__patch",
|
||||
"B:__patch",
|
||||
"A:__patch",
|
||||
"B:__patch(from __mount)",
|
||||
"C:__patch(from __mount)",
|
||||
"D:__patch(from __mount)",
|
||||
"E:__patch(from __mount)",
|
||||
"B:mounted",
|
||||
"D:mounted",
|
||||
"E:mounted",
|
||||
"D:mounted",
|
||||
"C:mounted",
|
||||
"B:mounted",
|
||||
"A:mounted"
|
||||
]);
|
||||
|
||||
@@ -2491,12 +2540,12 @@ describe("random stuff/miscellaneous", () => {
|
||||
"F:render",
|
||||
"C:willPatch",
|
||||
"D:willPatch",
|
||||
"F:__patch",
|
||||
"D:__patch",
|
||||
"C:__patch",
|
||||
"E:willUnmount",
|
||||
"E:destroy",
|
||||
"F:__patch(from __mount)",
|
||||
"F:mounted",
|
||||
"D:__patch",
|
||||
"D:patched",
|
||||
"C:patched"
|
||||
]);
|
||||
@@ -3599,6 +3648,43 @@ describe("async rendering", () => {
|
||||
expect(Parent.prototype.__render).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
|
||||
test("concurrent renderings scenario 13", async () => {
|
||||
let lastChild;
|
||||
class Child extends Component<any, any> {
|
||||
static template = xml`<span><t t-esc="state.val"/></span>`;
|
||||
state = useState({ val: 0 });
|
||||
mounted() {
|
||||
if (lastChild) {
|
||||
lastChild.state.val = 0;
|
||||
}
|
||||
lastChild = this;
|
||||
this.state.val = 1;
|
||||
}
|
||||
}
|
||||
|
||||
class Parent extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<Child/>
|
||||
<Child t-if="state.bool"/>
|
||||
</div>`;
|
||||
static components = { Child };
|
||||
state = useState({ bool: false });
|
||||
}
|
||||
|
||||
const parent = new Parent();
|
||||
await parent.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span>0</span></div>");
|
||||
|
||||
await nextTick(); // wait for changes triggered in mounted to be applied
|
||||
expect(fixture.innerHTML).toBe("<div><span>1</span></div>");
|
||||
|
||||
parent.state.bool = true;
|
||||
await nextTick(); // wait for this change to be applied
|
||||
await nextTick(); // wait for changes triggered in mounted to be applied
|
||||
expect(fixture.innerHTML).toBe("<div><span>0</span><span>1</span></div>");
|
||||
});
|
||||
|
||||
test("change state and call manually render: no unnecessary rendering", async () => {
|
||||
class Widget extends Component<any, any> {
|
||||
static template = xml`<div><t t-esc="state.val"/></div>`;
|
||||
@@ -4240,9 +4326,9 @@ describe("t-slot directive", () => {
|
||||
test("slots in t-foreach and re-rendering", async () => {
|
||||
class Child extends Widget {
|
||||
static template = xml`<span><t t-esc="state.val"/><t t-slot="default"/></span>`;
|
||||
state = useState({ val: 'A' });
|
||||
state = useState({ val: "A" });
|
||||
mounted() {
|
||||
this.state.val = 'B';
|
||||
this.state.val = "B";
|
||||
}
|
||||
}
|
||||
class Parent extends Widget {
|
||||
@@ -4269,9 +4355,9 @@ describe("t-slot directive", () => {
|
||||
<t t-esc="state.val"/>
|
||||
<t t-slot="default"/>
|
||||
</span>`;
|
||||
state = useState({ val: 'A' });
|
||||
state = useState({ val: "A" });
|
||||
mounted() {
|
||||
this.state.val = 'B';
|
||||
this.state.val = "B";
|
||||
}
|
||||
}
|
||||
class ParentWidget extends Widget {
|
||||
@@ -4752,7 +4838,7 @@ describe("component error handling (catchError)", () => {
|
||||
expect(handler).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test("can catch an error in the initial call of a component render function", async () => {
|
||||
test("can catch an error in the initial call of a component render function (parent mounted)", async () => {
|
||||
const handler = jest.fn();
|
||||
env.qweb.on("error", null, handler);
|
||||
const consoleError = console.error;
|
||||
@@ -4788,6 +4874,45 @@ describe("component error handling (catchError)", () => {
|
||||
expect(handler).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test("can catch an error in the initial call of a component render function (parent updated)", async () => {
|
||||
const handler = jest.fn();
|
||||
env.qweb.on("error", null, handler);
|
||||
const consoleError = console.error;
|
||||
console.error = jest.fn();
|
||||
class ErrorComponent extends Component<any, any> {
|
||||
static template = xml`<div>hey<t t-esc="state.this.will.crash"/></div>`;
|
||||
}
|
||||
class ErrorBoundary extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<t t-if="state.error">Error handled</t>
|
||||
<t t-else="1"><t t-slot="default" /></t>
|
||||
</div>`;
|
||||
state = useState({ error: false });
|
||||
|
||||
catchError() {
|
||||
this.state.error = true;
|
||||
}
|
||||
}
|
||||
class App extends Component<any, any> {
|
||||
static template = xml`
|
||||
<div>
|
||||
<ErrorBoundary t-if="state.flag"><ErrorComponent /></ErrorBoundary>
|
||||
</div>`;
|
||||
state = useState({ flag: false });
|
||||
static components = { ErrorBoundary, ErrorComponent };
|
||||
}
|
||||
const app = new App();
|
||||
await app.mount(fixture);
|
||||
app.state.flag = true;
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><div>Error handled</div></div>");
|
||||
|
||||
expect(console.error).toBeCalledTimes(0);
|
||||
console.error = consoleError;
|
||||
expect(handler).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
test("can catch an error in the constructor call of a component render function", async () => {
|
||||
const handler = jest.fn();
|
||||
env.qweb.on("error", null, handler);
|
||||
@@ -5458,6 +5583,73 @@ describe("unmounting and remounting", () => {
|
||||
expect(TestWidget.prototype.__patch).toHaveBeenCalledTimes(2);
|
||||
expect(steps).toEqual([2, 2, 3]);
|
||||
});
|
||||
|
||||
test("change state while component is unmounted", async () => {
|
||||
let child;
|
||||
class Child extends Component<any, any> {
|
||||
static template = xml`<span t-esc="state.val"/>`;
|
||||
state = useState({
|
||||
val: "C1"
|
||||
});
|
||||
constructor(parent, props) {
|
||||
super(parent, props);
|
||||
child = this;
|
||||
}
|
||||
}
|
||||
|
||||
class Parent extends Component<any, any> {
|
||||
static components = { Child };
|
||||
static template = xml`<div><t t-esc="state.val"/><Child/></div>`;
|
||||
state = useState({ val: "P1" });
|
||||
}
|
||||
|
||||
const parent = new Parent();
|
||||
await parent.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div>P1<span>C1</span></div>");
|
||||
|
||||
parent.unmount();
|
||||
expect(fixture.innerHTML).toBe("");
|
||||
|
||||
parent.state.val = "P2";
|
||||
child.state.val = "C2";
|
||||
|
||||
await parent.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div>P2<span>C2</span></div>");
|
||||
});
|
||||
|
||||
test("unmount component during a re-rendering", async () => {
|
||||
const def = makeDeferred();
|
||||
class Child extends Widget {
|
||||
static template = xml`<span><t t-esc="props.val"/></span>`;
|
||||
willUpdateProps() {
|
||||
return def;
|
||||
}
|
||||
}
|
||||
Child.prototype.__render = jest.fn(Child.prototype.__render);
|
||||
|
||||
class Parent extends Widget {
|
||||
static template = xml`<div><Child val="state.val"/></div>`;
|
||||
static components = { Child };
|
||||
state = useState({ val: 1 });
|
||||
}
|
||||
|
||||
const parent = new Parent();
|
||||
await parent.mount(fixture);
|
||||
expect(fixture.innerHTML).toBe("<div><span>1</span></div>");
|
||||
expect(Child.prototype.__render).toBeCalledTimes(1);
|
||||
|
||||
parent.state.val = 2;
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("<div><span>1</span></div>");
|
||||
|
||||
parent.unmount();
|
||||
expect(fixture.innerHTML).toBe("");
|
||||
|
||||
def.resolve();
|
||||
await nextTick();
|
||||
expect(fixture.innerHTML).toBe("");
|
||||
expect(Child.prototype.__render).toBeCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("dynamic root nodes", () => {
|
||||
|
||||
@@ -702,7 +702,8 @@ describe("t-call (template calling", () => {
|
||||
</div>
|
||||
</templates>
|
||||
`);
|
||||
const expected = "<div><div><div><div><span>cascade 2</span><span>cascade 1</span><span>cascade 0</span><span>hey</span> <span>yay</span></div></div></div></div>";
|
||||
const expected =
|
||||
"<div><div><div><div><span>cascade 2</span><span>cascade 1</span><span>cascade 0</span><span>hey</span> <span>yay</span></div></div></div></div>";
|
||||
expect(renderToString(qweb, "main")).toBe(expected);
|
||||
});
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ exports[`RouteComponent can render simple cases 1`] = `
|
||||
w8 = new W8(parent, props8);
|
||||
parent.__owl__.cmap[k9] = w8.__owl__.id;
|
||||
let def7 = w8.__prepare(extra.fiber, undefined, undefined);
|
||||
let pvnode = h('dummy', {key: k9, hook: {insert(vn) { let nvn=w8.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w8.destroy();}}});
|
||||
let pvnode = h('dummy', {key: k9, hook: {remove() {},destroy(vn) {w8.destroy();}}});
|
||||
const fiber = w8.__owl__.currentFiber;
|
||||
def7.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
|
||||
utils.defineProxy(vn11, pvnode);
|
||||
|
||||
Reference in New Issue
Block a user