[FIX] component: concurrent calls to mount and render

Closes #450
This commit is contained in:
Aaron Bohy
2019-11-05 14:23:06 +01:00
committed by Géry Debongnie
parent bbdc9d90d7
commit e5940b4b6b
11 changed files with 292 additions and 112 deletions
+29 -53
View File
@@ -1,6 +1,6 @@
import { Observer } from "../core/observer";
import { CompiledTemplate, QWeb } from "../qweb/index";
import { h, patch, VNode } from "../vdom/index";
import { patch, VNode } from "../vdom/index";
import "./directive";
import { Fiber } from "./fiber";
import "./props_validation";
@@ -282,32 +282,19 @@ export class Component<T extends Env, Props extends {}> {
return Promise.resolve();
}
if (!(target instanceof HTMLElement)) {
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);
}
return new Promise((resolve, reject) => {
const fiber = new Fiber(null, this, undefined, undefined, false);
scheduler.addFiber(fiber, err => {
if (err) {
reject(err);
return;
}
if (!__owl__.isDestroyed) {
this.__patch(fiber.vnode);
target.appendChild(this.el!);
if (document.body.contains(target)) {
this.__callMounted();
}
}
resolve();
});
if (!__owl__.vnode) {
this.__prepareAndRender(fiber);
} else {
this.__render(fiber);
}
});
const fiber = new Fiber(null, this, undefined, undefined, false, target);
if (!__owl__.vnode) {
this.__prepareAndRender(fiber);
} else {
this.__render(fiber);
}
return scheduler.addFiber(fiber);
}
/**
@@ -332,26 +319,18 @@ export class Component<T extends Env, Props extends {}> {
*/
async render(force: boolean = false): Promise<void> {
const __owl__ = this.__owl__;
if (
(!__owl__.isMounted && !__owl__.currentFiber) ||
(__owl__.currentFiber && !__owl__.currentFiber.isRendered)
) {
if (!__owl__.isMounted && !__owl__.currentFiber) {
// if we get here, this means that the component was either never mounted,
// or was unmounted and some state change triggered a render. Either way,
// we do not want to actually render anything in this case.
return;
}
return new Promise((resolve, reject) => {
const fiber = new Fiber(null, this, undefined, undefined, force);
scheduler.addFiber(fiber.root, err => {
if (err) {
reject(err);
return;
}
if (__owl__.isMounted && fiber === fiber.root) {
fiber.patchComponents();
}
resolve();
});
this.__render(fiber);
});
if (__owl__.currentFiber && !__owl__.currentFiber.isRendered) {
return scheduler.addFiber(__owl__.currentFiber.root);
}
const fiber = new Fiber(null, this, undefined, undefined, force, null);
this.__render(fiber);
return scheduler.addFiber(fiber);
}
/**
@@ -437,7 +416,7 @@ export class Component<T extends Env, Props extends {}> {
__owl__.isDestroyed = true;
delete __owl__.vnode;
if (__owl__.currentFiber) {
__owl__.currentFiber.isCancelled = true;
__owl__.currentFiber.isCompleted = true;
}
}
@@ -492,7 +471,7 @@ export class Component<T extends Env, Props extends {}> {
const shouldUpdate = parentFiber.force || this.shouldUpdate(nextProps);
if (shouldUpdate) {
const __owl__ = this.__owl__;
const fiber = new Fiber(parentFiber, this, scope, vars, parentFiber.force);
const fiber = new Fiber(parentFiber, this, scope, vars, parentFiber.force, null);
if (!parentFiber.child) {
parentFiber.child = fiber;
} else {
@@ -510,7 +489,7 @@ export class Component<T extends Env, Props extends {}> {
this.willUpdateProps(nextProps),
__owl__.willUpdatePropsCB && __owl__.willUpdatePropsCB(nextProps)
]);
if (fiber.isCancelled) {
if (fiber.isCompleted) {
return;
}
this.props = nextProps;
@@ -535,7 +514,7 @@ export class Component<T extends Env, Props extends {}> {
* parent template.
*/
__prepare(parentFiber: Fiber, scope: any, vars: any, previousSibling?: Fiber | null) {
const fiber = new Fiber(parentFiber, this, scope, vars, parentFiber.force);
const fiber = new Fiber(parentFiber, this, scope, vars, parentFiber.force, null);
fiber.shouldPatch = false;
if (!parentFiber.child) {
parentFiber.child = fiber;
@@ -569,13 +548,12 @@ export class Component<T extends Env, Props extends {}> {
await Promise.all([this.willStart(), this.__owl__.willStartCB && this.__owl__.willStartCB()]);
} catch (e) {
fiber.handleError(e);
fiber.vnode = h("div"); // -> we render this div at the end
return Promise.resolve();
}
if (this.__owl__.isDestroyed) {
return Promise.resolve();
}
if (!fiber.isCancelled) {
if (!fiber.isCompleted) {
this.__render(fiber);
}
}
@@ -585,17 +563,14 @@ export class Component<T extends Env, Props extends {}> {
if (__owl__.observer) {
__owl__.observer.allowMutations = false;
}
let vnode;
try {
vnode = __owl__.renderFn!(this, {
fiber.vnode = __owl__.renderFn!(this, {
handlers: __owl__.boundHandlers,
fiber: fiber
});
} catch (e) {
vnode = __owl__.vnode || h("div");
fiber.handleError(e);
}
fiber.vnode = vnode;
if (__owl__.observer) {
__owl__.observer.allowMutations = true;
}
@@ -604,7 +579,8 @@ export class Component<T extends Env, Props extends {}> {
// template (so, something like <MyComponent class="..."/>) to the actual
// root vnode
if (__owl__.classObj) {
vnode.data.class = Object.assign(vnode.data.class || {}, __owl__.classObj);
const data = fiber.vnode!.data!;
data.class = Object.assign(data.class || {}, __owl__.classObj);
}
fiber.root.counter--;
fiber.isRendered = true;
+1 -1
View File
@@ -455,7 +455,7 @@ QWeb.addDirective({
);
ctx.addLine(`const fiber = w${componentID}.__owl__.currentFiber;`);
ctx.addLine(
`def${defID}.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; ${createHook}});`
`def${defID}.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; ${createHook}});`
);
if (registerCode) {
ctx.addLine(registerCode);
+65 -13
View File
@@ -1,4 +1,4 @@
import { VNode } from "../vdom/index";
import { h, VNode } from "../vdom/index";
import { Component } from "./component";
import { scheduler } from "./scheduler";
@@ -20,9 +20,10 @@ export class Fiber {
// method potentially implemented by a component. It is usually set to false.
force: boolean;
// isCancelled means that the rendering corresponding to this fiber and its
// children is cancelled. No extra work should be done.
isCancelled: boolean = false;
// isCompleted means that the rendering corresponding to this fiber's work is
// done, either because the component has been mounted or patched, or because
// fiber has been cancelled.
isCompleted: boolean = false;
// the fibers corresponding to component updates (updateProps) need to call
// the willPatch and patched hooks from the corresponding component. However,
@@ -42,6 +43,8 @@ export class Fiber {
// scheduler.
counter: number = 0;
target: HTMLElement | null;
scope: any;
vars: any;
@@ -55,18 +58,25 @@ export class Fiber {
error?: Error;
constructor(parent: Fiber | null, component: Component<any, any>, scope, vars, force) {
constructor(parent: Fiber | null, component: Component<any, any>, scope, vars, force, target) {
this.force = force;
this.scope = scope;
this.vars = vars;
this.component = component;
this.target = target;
this.root = parent ? parent.root : this;
this.parent = parent;
let oldFiber = component.__owl__.currentFiber;
if (oldFiber && !oldFiber.isCancelled) {
this._remapFiber(oldFiber);
if (oldFiber && !oldFiber.isCompleted) {
if (oldFiber.root === oldFiber && !parent) {
// both oldFiber and this fiber are root fibers
this._reuseFiber(oldFiber);
return oldFiber;
} else {
this._remapFiber(oldFiber);
}
}
this.root.counter++;
@@ -74,6 +84,25 @@ export class Fiber {
component.__owl__.currentFiber = this;
}
/**
* When the oldFiber is not completed yet, and both oldFiber and this fiber
* are root fibers, we want to reuse the oldFiber instead of creating a new
* one. Doing so will guarantee that the initiator(s) of those renderings will
* be notified (the promise will resolve) when the last rendering will be done.
*
* This function thus assumes that oldFiber is a root fiber.
*/
_reuseFiber(oldFiber: Fiber) {
oldFiber.cancel(); // cancel children fibers
oldFiber.isCompleted = false; // keep the root fiber alive
if (oldFiber.child) {
// remove relation to children
oldFiber.child.parent = null;
oldFiber.child = null;
}
oldFiber.counter = 1; // re-initialize counter
}
/**
* In some cases, a rendering initiated at some component can detect that it
* should be part of a larger rendering initiated somewhere up the component
@@ -83,7 +112,7 @@ export class Fiber {
_remapFiber(oldFiber: Fiber) {
oldFiber.cancel();
if (oldFiber === oldFiber.root) {
oldFiber.root.counter++;
oldFiber.counter++;
}
if (oldFiber.parent && !this.parent) {
// re-map links
@@ -132,7 +161,28 @@ export class Fiber {
}
/**
* Apply the given patch queue from a fiber.
* Successfully complete the work of the fiber: call the mount or patch hooks
* and patch the DOM. This function is called once the fiber and its children
* 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();
}
}
this.isCompleted = true;
}
/**
* 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
@@ -142,8 +192,8 @@ export class Fiber {
const doWork: (Fiber) => Fiber | null = function(f) {
if (f.shouldPatch) {
patchQueue.push(f);
return f.child;
}
return f.child;
};
this._walk(doWork);
let component: Component<any, any> = this.component;
@@ -186,7 +236,7 @@ export class Fiber {
if (!f.isRendered) {
f.root.counter--;
}
f.isCancelled = true;
f.isCompleted = true;
return f.child;
});
}
@@ -200,10 +250,12 @@ export class Fiber {
* being in a corrupted state.
*/
handleError(error: Error) {
let canCatch = false;
let component = this.component;
let qweb = component.env.qweb;
this.vnode = component.__owl__.vnode || h("div");
const qweb = component.env.qweb;
let root = component;
let canCatch = false;
while (component && !(canCatch = !!component.catchError)) {
root = component;
component = component.__owl__.parent!;
+25 -8
View File
@@ -25,12 +25,25 @@ export class Scheduler {
this.requestAnimationFrame = requestAnimationFrame;
}
addFiber(fiber, callback) {
this.tasks.push({ fiber, callback });
if (this.isRunning) {
return;
}
this.scheduleTasks();
addFiber(fiber): Promise<void> {
return new Promise((resolve, reject) => {
if (fiber.error) {
return reject(fiber.error);
}
this.tasks.push({
fiber,
callback: () => {
if (fiber.error) {
reject(fiber.error);
return;
}
resolve();
}
});
if (!this.isRunning) {
this.scheduleTasks();
}
});
}
/**
@@ -41,11 +54,15 @@ export class Scheduler {
let tasks = this.tasks;
this.tasks = [];
tasks = tasks.filter(task => {
if (task.fiber.isCancelled) {
if (task.fiber.isCompleted) {
task.callback();
return false;
}
if (task.fiber.counter === 0) {
task.callback(task.fiber.error);
if (!task.fiber.error) {
task.fiber.complete();
}
task.callback();
return false;
}
return true;
+11 -1
View File
@@ -24,9 +24,19 @@ import { Observer } from "./core/observer";
export function useState<T>(state: T): T {
const component: Component<any, any> = Component.current!;
const __owl__ = component.__owl__;
const renderFn = __owl__.renderFn;
let lastRenderRevNumber;
__owl__.renderFn = function(comp, params) {
lastRenderRevNumber = __owl__.observer!.rev;
return renderFn(comp, params);
};
if (!__owl__.observer) {
__owl__.observer = new Observer();
__owl__.observer.notifyCB = component.render.bind(component);
__owl__.observer.notifyCB = () => {
if (lastRenderRevNumber < __owl__.observer!.rev) {
component.render();
}
};
}
return __owl__.observer.observe(state);
}
+6 -2
View File
@@ -236,7 +236,9 @@ QWeb.addDirective({
varCode = `{${content}}`;
}
ctx.addLine(
`this.recursiveFns['${subTemplateName}'].call(this, context, Object.assign({}, extra, {parentNode: c${ctx.parentNode}, fiber: {vars: ${varCode}, scope}}));`
`this.recursiveFns['${subTemplateName}'].call(this, context, Object.assign({}, extra, {parentNode: c${
ctx.parentNode
}, fiber: {vars: ${varCode}, scope}}));`
);
return true;
}
@@ -305,7 +307,9 @@ QWeb.addDirective({
!node.children[0].hasAttribute("t-key");
if (shouldWarn) {
console.warn(
`Directive t-foreach should always be used with a t-key! (in template: '${ctx.templateName}')`
`Directive t-foreach should always be used with a t-key! (in template: '${
ctx.templateName
}')`
);
}
+2 -2
View File
@@ -35,7 +35,7 @@ exports[`animations t-transition combined with component 1`] = `
};
utils.transitionRemove(vn, 'chimay', finalize);}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
def2.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
c1.push(pvnode);
w3.__owl__.pvnode = pvnode;
}
@@ -80,7 +80,7 @@ exports[`animations t-transition combined with t-component and t-if 1`] = `
};
utils.transitionRemove(vn, 'chimay', finalize);}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
def2.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
c1.push(pvnode);
w3.__owl__.pvnode = pvnode;
}
@@ -61,7 +61,7 @@ exports[`basic widget properties reconciliation alg works for t-foreach in t-for
let def8 = w9.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId9, hook: {insert(vn) { let nvn=w9.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w9.destroy();}}});
const fiber = w9.__owl__.currentFiber;
def8.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
def8.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
c1.push(pvnode);
w9.__owl__.pvnode = pvnode;
}
@@ -105,7 +105,7 @@ exports[`class and style attributes with t-component dynamic t-att-style is prop
let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.style = _5;}};});
def2.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.style = _5;}};});
c1.push(pvnode);
w3.__owl__.pvnode = pvnode;
}
@@ -150,7 +150,7 @@ exports[`class and style attributes with t-component t-att-class is properly add
let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;context.__owl__.refs[ref5] = w3;},remove() {},destroy(vn) {w3.destroy();delete context.__owl__.refs[ref5];}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){}};});
def2.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){}};});
c1.push(pvnode);
w3.__owl__.pvnode = pvnode;
}
@@ -210,7 +210,7 @@ exports[`class and style attributes with t-component t-att-class is properly add
let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;context.__owl__.refs[ref5] = w3;},remove() {},destroy(vn) {w3.destroy();delete context.__owl__.refs[ref5];}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){}};});
def2.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){}};});
c1.push(pvnode);
w3.__owl__.pvnode = pvnode;
}
@@ -282,7 +282,7 @@ exports[`composition sub components with some state rendered in a loop 1`] = `
let def6 = w7.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId7, hook: {insert(vn) { let nvn=w7.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w7.destroy();}}});
const fiber = w7.__owl__.currentFiber;
def6.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
def6.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
c1.push(pvnode);
w7.__owl__.pvnode = pvnode;
}
@@ -324,7 +324,7 @@ exports[`composition t-component with dynamic value 1`] = `
let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
def2.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
c1.push(pvnode);
w3.__owl__.pvnode = pvnode;
}
@@ -365,7 +365,7 @@ exports[`composition t-component with dynamic value 2 1`] = `
let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
def2.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
c1.push(pvnode);
w3.__owl__.pvnode = pvnode;
}
@@ -406,7 +406,7 @@ exports[`dynamic t-props basic use 1`] = `
let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
def2.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
c1.push(pvnode);
w3.__owl__.pvnode = pvnode;
}
@@ -451,7 +451,7 @@ exports[`other directives with t-component t-on with getter as handler 1`] = `
let def3 = w4.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId4, hook: {insert(vn) { let nvn=w4.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});
const fiber = w4.__owl__.currentFiber;
def3.then(function () {if (fiber.isCancelled) {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; }});}};});
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);
w4.__owl__.pvnode = pvnode;
}
@@ -492,7 +492,7 @@ exports[`other directives with t-component t-on with handler bound to argument 1
let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (fiber.isCancelled) {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; }});}};});
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);
w3.__owl__.pvnode = pvnode;
}
@@ -533,7 +533,7 @@ exports[`other directives with t-component t-on with handler bound to empty obje
let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (fiber.isCancelled) {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; }});}};});
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);
w3.__owl__.pvnode = pvnode;
}
@@ -574,7 +574,7 @@ exports[`other directives with t-component t-on with handler bound to empty obje
let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (fiber.isCancelled) {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; }});}};});
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);
w3.__owl__.pvnode = pvnode;
}
@@ -615,7 +615,7 @@ exports[`other directives with t-component t-on with handler bound to object 1`]
let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (fiber.isCancelled) {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; }});}};});
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);
w3.__owl__.pvnode = pvnode;
}
@@ -660,7 +660,7 @@ exports[`other directives with t-component t-on with inline statement 1`] = `
let def3 = w4.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId4, hook: {insert(vn) { let nvn=w4.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});
const fiber = w4.__owl__.currentFiber;
def3.then(function () {if (fiber.isCancelled) {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++; }});}};});
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);
w4.__owl__.pvnode = pvnode;
}
@@ -701,7 +701,7 @@ exports[`other directives with t-component t-on with no handler (only modifiers)
let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (fiber.isCancelled) {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; }});}};});
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);
w3.__owl__.pvnode = pvnode;
}
@@ -742,7 +742,7 @@ exports[`other directives with t-component t-on with prevent and self modifiers
let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (fiber.isCancelled) {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; }});}};});
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);
w3.__owl__.pvnode = pvnode;
}
@@ -783,7 +783,7 @@ exports[`other directives with t-component t-on with self and prevent modifiers
let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (fiber.isCancelled) {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; }});}};});
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);
w3.__owl__.pvnode = pvnode;
}
@@ -824,7 +824,7 @@ exports[`other directives with t-component t-on with self modifier 1`] = `
let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (fiber.isCancelled) {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; }});}};});
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);
w3.__owl__.pvnode = pvnode;
}
@@ -865,7 +865,7 @@ exports[`other directives with t-component t-on with stop and/or prevent modifie
let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (fiber.isCancelled) {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; }});}};});
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);
w3.__owl__.pvnode = pvnode;
}
@@ -925,7 +925,7 @@ exports[`random stuff/miscellaneous snapshotting compiled code 1`] = `
let def3 = w4.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId4, hook: {insert(vn) { let nvn=w4.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});
const fiber = w4.__owl__.currentFiber;
def3.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
def3.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
c1.push(pvnode);
w4.__owl__.pvnode = pvnode;
}
@@ -983,7 +983,7 @@ exports[`random stuff/miscellaneous t-on with handler bound to dynamic argument
let def6 = w7.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId7, hook: {insert(vn) { let nvn=w7.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w7.destroy();}}});
const fiber = w7.__owl__.currentFiber;
def6.then(function () {if (fiber.isCancelled) {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, arg9, e); } else { owner.onEv; }});}};});
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, arg9, e); } else { owner.onEv; }});}};});
c1.push(pvnode);
w7.__owl__.pvnode = pvnode;
}
@@ -1231,7 +1231,7 @@ exports[`t-slot directive can define and call slots 1`] = `
let def2 = w3.__prepare(extra.fiber, {}, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
def2.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
c1.push(pvnode);
w3.__owl__.pvnode = pvnode;
}
@@ -1477,7 +1477,7 @@ exports[`t-slot directive slots are rendered with proper context, part 2 2`] = `
let def8 = w9.__prepare(extra.fiber, Object.assign({}, scope), undefined, sibling);
let pvnode = h('dummy', {key: templateId9, hook: {insert(vn) { let nvn=w9.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w9.destroy();}}});
const fiber = w9.__owl__.currentFiber;
def8.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
def8.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
c7.push(pvnode);
w9.__owl__.pvnode = pvnode;
}
@@ -1581,7 +1581,7 @@ exports[`t-slot directive slots are rendered with proper context, part 3 2`] = `
let def9 = w10.__prepare(extra.fiber, Object.assign({}, scope), {_8}, sibling);
let pvnode = h('dummy', {key: templateId10, hook: {insert(vn) { let nvn=w10.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w10.destroy();}}});
const fiber = w10.__owl__.currentFiber;
def9.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
def9.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
c7.push(pvnode);
w10.__owl__.pvnode = pvnode;
}
@@ -1639,7 +1639,7 @@ exports[`t-slot directive slots are rendered with proper context, part 4 1`] = `
let def3 = w4.__prepare(extra.fiber, {}, {_2}, sibling);
let pvnode = h('dummy', {key: templateId4, hook: {insert(vn) { let nvn=w4.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w4.destroy();}}});
const fiber = w4.__owl__.currentFiber;
def3.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
def3.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
c1.push(pvnode);
w4.__owl__.pvnode = pvnode;
}
@@ -1714,7 +1714,7 @@ exports[`top level sub widgets basic use 1`] = `
let def1 = w2.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId2, hook: {insert(vn) { let nvn=w2.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w2.destroy();}}});
const fiber = w2.__owl__.currentFiber;
def1.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
def1.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
utils.defineProxy(vn4, pvnode);
w2.__owl__.pvnode = pvnode;
}
@@ -1757,7 +1757,7 @@ exports[`top level sub widgets can select a sub widget 1`] = `
let def1 = w2.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId2, hook: {insert(vn) { let nvn=w2.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w2.destroy();}}});
const fiber = w2.__owl__.currentFiber;
def1.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
def1.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
utils.defineProxy(vn4, pvnode);
w2.__owl__.pvnode = pvnode;
}
@@ -1787,7 +1787,7 @@ exports[`top level sub widgets can select a sub widget 1`] = `
let def5 = w6.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId6, hook: {insert(vn) { let nvn=w6.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w6.destroy();}}});
const fiber = w6.__owl__.currentFiber;
def5.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
def5.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
utils.defineProxy(vn8, pvnode);
w6.__owl__.pvnode = pvnode;
}
@@ -32,7 +32,7 @@ exports[`props validation props are validated in dev mode (code snapshot) 1`] =
let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
def2.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
c1.push(pvnode);
w3.__owl__.pvnode = pvnode;
}
+123 -2
View File
@@ -3348,7 +3348,6 @@ describe("async rendering", () => {
const componentA = new ComponentA();
await componentA.mount(fixture);
expect(fixture.innerHTML).toBe("<div><p></p></div>");
stateB.hasChild = true;
await nextTick();
expect(fixture.innerHTML).toBe("<div><p></p></div>");
@@ -3363,6 +3362,64 @@ describe("async rendering", () => {
expect(fixture.innerHTML).toBe("<div><p><span>2</span></p></div>");
expect(ComponentC.prototype.__render).toHaveBeenCalledTimes(1);
});
test("concurrent renderings scenario 11", async () => {
// This scenario is the following: we have a component being updated (by props),
// and then rendered (render method), but before the willUpdateProps resolves.
// We check that in that case, the return value of the render method is a promise
// that is resolved when the component is completely rendered (so, properly
// remapped to the promise of the ambient rendering)
const def = makeDeferred();
let child;
class Child extends Component<any, any> {
static template = xml`<span><t t-esc="props.val"/>|<t t-esc="val"/></span>`;
val = 3;
willUpdateProps() {
child = this;
return def;
}
}
class Parent extends Component<any, any> {
static template = xml`<div><Child val="state.valA"/></div>`;
static components = { Child };
state = useState({ valA: 1 });
}
const parent = new Parent();
await parent.mount(fixture);
expect(fixture.innerHTML).toBe("<div><span>1|3</span></div>");
parent.state.valA = 2;
await nextTick();
setTimeout(() => {
def.resolve();
}, 20);
child.val = 5;
await child.render();
expect(fixture.innerHTML).toBe("<div><span>2|5</span></div>");
});
test("change state and call manually render: no unnecessary rendering", async () => {
// when the state is changed, the component isn't notified directly (we wait
// for a microtask tick before calling 'render'), so it may happen that
// another rendering is done meanwhile, already using the new value of the
// state
class Widget extends Component<any, any> {
static template = xml`<div><t t-esc="state.val"/></div>`;
state = useState({ val: 1 });
}
Widget.prototype.__render = jest.fn(Widget.prototype.__render);
const widget = new Widget();
await widget.mount(fixture);
expect(fixture.innerHTML).toBe("<div>1</div>");
expect(Widget.prototype.__render).toHaveBeenCalledTimes(1);
widget.state.val = 2;
await widget.render();
expect(fixture.innerHTML).toBe("<div>2</div>");
expect(Widget.prototype.__render).toHaveBeenCalledTimes(2);
});
});
describe("widget and observable state", () => {
@@ -4890,7 +4947,7 @@ describe("unmounting and remounting", () => {
expect(fixture.innerHTML).toBe("<div>3</div>");
});
test("sub component is still active after being unmounted and remounted", async () => {
test("sub component is still active after being unmounted and remounted", async () => {
class Child extends Component<any, any> {
static template = xml`
<p t-on-click="state.value++">
@@ -4920,6 +4977,70 @@ describe("unmounting and remounting", () => {
await nextTick();
expect(fixture.innerHTML).toBe("<div><p>3</p></div>");
});
test("change state just before mounting component", async () => {
const steps: number[] = [];
class TestWidget extends Component<any, any> {
static template = xml`
<div><t t-esc="state.val"/></div>
`;
state = useState({ val: 1 });
__render(f) {
steps.push(this.state.val);
return super.__render(f);
}
}
TestWidget.prototype.__render = jest.fn(TestWidget.prototype.__render);
const widget = new TestWidget();
widget.state.val = 2;
await widget.mount(fixture);
expect(fixture.innerHTML).toBe("<div>2</div>");
expect(TestWidget.prototype.__render).toHaveBeenCalledTimes(1);
// unmount and re-mount, as in this case, willStart won't be called, so it's
// slightly different
widget.unmount();
widget.state.val = 3;
await widget.mount(fixture);
expect(fixture.innerHTML).toBe("<div>3</div>");
expect(TestWidget.prototype.__render).toHaveBeenCalledTimes(2);
expect(steps).toEqual([2, 3]);
});
test("change state while mounting component", async () => {
const steps: number[] = [];
class TestWidget extends Component<any, any> {
static template = xml`
<div><t t-esc="state.val"/></div>
`;
state = useState({ val: 1 });
__render(f) {
steps.push(this.state.val);
return super.__render(f);
}
}
TestWidget.prototype.__render = jest.fn(TestWidget.prototype.__render);
TestWidget.prototype.__patch = jest.fn(TestWidget.prototype.__patch);
const widget = new TestWidget();
let prom = widget.mount(fixture);
widget.state.val = 2;
await prom;
expect(fixture.innerHTML).toBe("<div>2</div>");
expect(TestWidget.prototype.__render).toHaveBeenCalledTimes(1);
// unmount and re-mount, as in this case, willStart won't be called, so it's
// slightly different
widget.unmount();
prom = widget.mount(fixture);
widget.state.val = 3;
await prom;
expect(fixture.innerHTML).toBe("<div>3</div>");
expect(TestWidget.prototype.__render).toHaveBeenCalledTimes(3);
expect(TestWidget.prototype.__patch).toHaveBeenCalledTimes(2);
expect(steps).toEqual([2, 2, 3]);
});
});
describe("dynamic root nodes", () => {
@@ -35,7 +35,7 @@ exports[`RouteComponent can render simple cases 1`] = `
let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling);
let pvnode = h('dummy', {key: templateId3, hook: {insert(vn) { let nvn=w3.__mount(fiber, pvnode.elm);pvnode.elm=nvn.elm;},remove() {},destroy(vn) {w3.destroy();}}});
const fiber = w3.__owl__.currentFiber;
def2.then(function () {if (fiber.isCancelled) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
def2.then(function () {if (fiber.isCompleted) {return;} const vnode = fiber.vnode; pvnode.sel = vnode.sel; });
utils.defineProxy(vn5, pvnode);
w3.__owl__.pvnode = pvnode;
}