From 2e3e8cd603eaf2b102f35df2927526f2160d3b81 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Wed, 20 Nov 2019 13:19:14 +0100 Subject: [PATCH] [FIX] component: destroy not yet mounted components (2) Following 2922cee6ea9b9f Previous commit was not correct: used children with a cancelled fiber (for instance, with a new currentFiber) could be destroyed. This commit fixes the issue differently, by directly detecting children that are not used anymore (and not mounted), and destroy them directly (no need to wait for willStart promise to be resolved anymore). --- src/component/component.ts | 14 ++++ src/component/directive.ts | 3 +- src/component/fiber.ts | 4 + tests/__snapshots__/animations.test.ts.snap | 6 +- .../__snapshots__/component.test.ts.snap | 84 ++++++++++++------- .../props_validation.test.ts.snap | 3 +- tests/component/component.test.ts | 9 +- tests/context.test.ts | 5 +- .../route_component.test.ts.snap | 3 +- 9 files changed, 86 insertions(+), 45 deletions(-) diff --git a/src/component/component.ts b/src/component/component.ts index 1a272efe..c72879b6 100644 --- a/src/component/component.ts +++ b/src/component/component.ts @@ -60,6 +60,9 @@ interface Internal { cmap: { [key: number]: number }; currentFiber: Fiber | null; + // parentLastFiberId is there to help the parent component to detect, among + // its children, those that are not used anymore and thus can be destroyed + parentLastFiberId: number; boundHandlers: { [key: number]: any }; observer: Observer | null; @@ -169,6 +172,7 @@ export class Component { children: {}, cmap: {}, currentFiber: null, + parentLastFiberId: 0, boundHandlers: {}, mountedCB: null, willUnmountCB: null, @@ -579,6 +583,16 @@ export class Component { handlers: __owl__.boundHandlers, fiber: fiber }); + // we iterate over the children to detect those that no longer belong to the + // current rendering: those ones, if not mounted yet, can (and have to) be + // destroyed right now, because they are not in the DOM, and thus we won't + // be notified later on (when patching), that they are removed from the DOM + for (let childKey in __owl__.children) { + let child = __owl__.children[childKey]; + if (!child.__owl__.isMounted && child.__owl__.parentLastFiberId < fiber.id) { + child.destroy(); + } + } if (!vnode) { throw new Error(`Rendering '${this.constructor.name}' did not return anything`); } diff --git a/src/component/directive.ts b/src/component/directive.ts index ed293df9..a00ca646 100644 --- a/src/component/directive.ts +++ b/src/component/directive.ts @@ -443,7 +443,7 @@ QWeb.addDirective({ ); ctx.addLine(`const fiber = w${componentID}.__owl__.currentFiber;`); ctx.addLine( - `def${defID}.then(function () { if (fiber.isCompleted) { w${componentID}.destroy(); 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); @@ -459,6 +459,7 @@ QWeb.addDirective({ ctx.addLine(`w${componentID}.__owl__.classObj=${classObj};`); } + ctx.addLine(`w${componentID}.__owl__.parentLastFiberId = extra.fiber.id;`); ctx.addLine(`sibling = w${componentID}.__owl__.currentFiber || sibling;`); return true; diff --git a/src/component/fiber.ts b/src/component/fiber.ts index 0afc9e55..a761cc48 100644 --- a/src/component/fiber.ts +++ b/src/component/fiber.ts @@ -16,6 +16,9 @@ import { scheduler } from "./scheduler"; */ export class Fiber { + static nextId: number = 1; + id: number = Fiber.nextId++; + // The force attribute determines if a rendering should bypass the `shouldUpdate` // method potentially implemented by a component. It is usually set to false. force: boolean; @@ -101,6 +104,7 @@ export class Fiber { oldFiber.child = null; } oldFiber.counter = 1; // re-initialize counter + oldFiber.id = Fiber.nextId++; } /** diff --git a/tests/__snapshots__/animations.test.ts.snap b/tests/__snapshots__/animations.test.ts.snap index 3647b7a3..ba83ef2f 100644 --- a/tests/__snapshots__/animations.test.ts.snap +++ b/tests/__snapshots__/animations.test.ts.snap @@ -35,10 +35,11 @@ exports[`animations t-transition combined with component 1`] = ` }; utils.transitionRemove(vn, 'chimay', finalize);}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); 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; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -80,10 +81,11 @@ 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.isCompleted) { w3.destroy(); 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; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; } return vn1; diff --git a/tests/component/__snapshots__/component.test.ts.snap b/tests/component/__snapshots__/component.test.ts.snap index c797c165..ef8b7ecc 100644 --- a/tests/component/__snapshots__/component.test.ts.snap +++ b/tests/component/__snapshots__/component.test.ts.snap @@ -61,10 +61,11 @@ 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: k10, 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.isCompleted) { w9.destroy(); 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; } + w9.__owl__.parentLastFiberId = extra.fiber.id; sibling = w9.__owl__.currentFiber || sibling; } } @@ -105,10 +106,11 @@ 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: k4, 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.isCompleted) { w3.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; vnode.data.hook = {create(_, vn){vn.elm.style = _6;}};}); + 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); w3.__owl__.pvnode = pvnode; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -150,11 +152,12 @@ 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: 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];}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); 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; } w3.__owl__.classObj=_7; + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -210,11 +213,12 @@ 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: 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];}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); 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; } w3.__owl__.classObj=_7; + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -282,10 +286,11 @@ 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: k8, 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.isCompleted) { w7.destroy(); 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; } + w7.__owl__.parentLastFiberId = extra.fiber.id; sibling = w7.__owl__.currentFiber || sibling; } return vn1; @@ -324,10 +329,11 @@ exports[`composition t-component with dynamic value 1`] = ` let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); 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();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); 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; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -365,10 +371,11 @@ exports[`composition t-component with dynamic value 2 1`] = ` let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); 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();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); 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; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -406,10 +413,11 @@ exports[`dynamic t-props basic use 1`] = ` let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); 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();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); 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; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -451,10 +459,11 @@ 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: k5, 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.isCompleted) { w4.destroy(); 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; } + w4.__owl__.parentLastFiberId = extra.fiber.id; sibling = w4.__owl__.currentFiber || sibling; return vn1; }" @@ -492,10 +501,11 @@ 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: k4, 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.isCompleted) { w3.destroy(); 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; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -533,10 +543,11 @@ 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: k4, 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.isCompleted) { w3.destroy(); 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; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -574,10 +585,11 @@ 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: k4, 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.isCompleted) { w3.destroy(); 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; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -615,10 +627,11 @@ 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: k4, 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.isCompleted) { w3.destroy(); 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; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -660,10 +673,11 @@ 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: k5, 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.isCompleted) { w4.destroy(); 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; } + w4.__owl__.parentLastFiberId = extra.fiber.id; sibling = w4.__owl__.currentFiber || sibling; return vn1; }" @@ -701,10 +715,11 @@ 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: k4, 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.isCompleted) { w3.destroy(); 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; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -742,10 +757,11 @@ 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: k4, 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.isCompleted) { w3.destroy(); 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; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -783,10 +799,11 @@ 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: k4, 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.isCompleted) { w3.destroy(); 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; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -824,10 +841,11 @@ 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: k4, 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.isCompleted) { w3.destroy(); 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; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -865,10 +883,11 @@ 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: k4, 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.isCompleted) { w3.destroy(); 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; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -925,10 +944,11 @@ exports[`random stuff/miscellaneous snapshotting compiled code 1`] = ` let def3 = w4.__prepare(extra.fiber, undefined, undefined, sibling); 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();}}}); const fiber = w4.__owl__.currentFiber; - def3.then(function () { if (fiber.isCompleted) { w4.destroy(); 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; } + w4.__owl__.parentLastFiberId = extra.fiber.id; sibling = w4.__owl__.currentFiber || sibling; return vn1; }" @@ -983,10 +1003,11 @@ 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: k8, 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.isCompleted) { w7.destroy(); 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; }});}};}); + 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); w7.__owl__.pvnode = pvnode; } + w7.__owl__.parentLastFiberId = extra.fiber.id; sibling = w7.__owl__.currentFiber || sibling; } return vn1; @@ -1284,10 +1305,11 @@ exports[`t-slot directive can define and call slots 1`] = ` let def2 = w3.__prepare(extra.fiber, {}, undefined, sibling); 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();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); 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; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" @@ -1530,10 +1552,11 @@ 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: k10, 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.isCompleted) { w9.destroy(); 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; } + w9.__owl__.parentLastFiberId = extra.fiber.id; sibling = w9.__owl__.currentFiber || sibling; } return vn1; @@ -1634,10 +1657,11 @@ 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: k11, 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.isCompleted) { w10.destroy(); 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; } + w10.__owl__.parentLastFiberId = extra.fiber.id; sibling = w10.__owl__.currentFiber || sibling; } return vn1; @@ -1692,10 +1716,11 @@ 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: k5, 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.isCompleted) { w4.destroy(); 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; } + w4.__owl__.parentLastFiberId = extra.fiber.id; sibling = w4.__owl__.currentFiber || sibling; return vn1; }" @@ -1767,10 +1792,11 @@ exports[`top level sub widgets basic use 1`] = ` let def1 = w2.__prepare(extra.fiber, undefined, undefined, sibling); 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();}}}); const fiber = w2.__owl__.currentFiber; - def1.then(function () { if (fiber.isCompleted) { w2.destroy(); 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(vn5, pvnode); w2.__owl__.pvnode = pvnode; } + w2.__owl__.parentLastFiberId = extra.fiber.id; sibling = w2.__owl__.currentFiber || sibling; return result; }" @@ -1810,10 +1836,11 @@ 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: k3, 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.isCompleted) { w2.destroy(); 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(vn5, pvnode); w2.__owl__.pvnode = pvnode; } + w2.__owl__.parentLastFiberId = extra.fiber.id; sibling = w2.__owl__.currentFiber || sibling; } if (!context['env'].flag) { @@ -1840,10 +1867,11 @@ exports[`top level sub widgets can select a sub widget 1`] = ` let def6 = w7.__prepare(extra.fiber, undefined, undefined, sibling); 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();}}}); const fiber = w7.__owl__.currentFiber; - def6.then(function () { if (fiber.isCompleted) { w7.destroy(); return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); + def6.then(function () { if (fiber.isCompleted) { return; } const vnode = fiber.vnode; pvnode.sel = vnode.sel; }); utils.defineProxy(vn10, pvnode); w7.__owl__.pvnode = pvnode; } + w7.__owl__.parentLastFiberId = extra.fiber.id; sibling = w7.__owl__.currentFiber || sibling; } return result; diff --git a/tests/component/__snapshots__/props_validation.test.ts.snap b/tests/component/__snapshots__/props_validation.test.ts.snap index 17dd7e5b..d909abf4 100644 --- a/tests/component/__snapshots__/props_validation.test.ts.snap +++ b/tests/component/__snapshots__/props_validation.test.ts.snap @@ -32,10 +32,11 @@ 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: k4, 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.isCompleted) { w3.destroy(); 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; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; return vn1; }" diff --git a/tests/component/component.test.ts b/tests/component/component.test.ts index 80ebc8bd..507ef5cf 100644 --- a/tests/component/component.test.ts +++ b/tests/component/component.test.ts @@ -977,13 +977,12 @@ describe("destroy method", () => { }); test("destroying a widget before being mounted (2)", async () => { - const def = makeDeferred(); const steps: string[] = []; class Child extends Component { static template = xml``; willStart() { steps.push("willStart"); - return def; + return makeDeferred(); } __destroy(parent) { steps.push("__destroy"); @@ -1002,12 +1001,6 @@ describe("destroy method", () => { parent.state.flag = false; await prom; expect(fixture.innerHTML).toBe("
"); - // we can't easily detect that we have to destroy the component before - // willStart is resolved. However, willStart should have no side-effect - // (like dispatching an action), so it's probably fine - expect(steps).toEqual(["willStart"]); - def.resolve(); - await nextTick(); expect(steps).toEqual(["willStart", "__destroy"]); }); }); diff --git a/tests/context.test.ts b/tests/context.test.ts index a688df01..5be3b1de 100644 --- a/tests/context.test.ts +++ b/tests/context.test.ts @@ -257,13 +257,12 @@ describe("Context", () => { test("destroyed component before being mounted is inactive", async () => { const testContext = new Context({ a: 123 }); - const def = makeDeferred(); class Child extends Component { static template = xml``; contextObj = useContext(testContext); willStart() { - return def; + return makeDeferred(); } } class Parent extends Component { @@ -279,8 +278,6 @@ describe("Context", () => { parent.state.flag = false; await prom; expect(fixture.innerHTML).toBe("
"); - def.resolve(); // must wait for willStart promise to be resolved - await nextTick(); // kind of whitebox... // we make sure we do not have any pending subscriptions to the 'update' // event diff --git a/tests/router/__snapshots__/route_component.test.ts.snap b/tests/router/__snapshots__/route_component.test.ts.snap index 140af816..120a999b 100644 --- a/tests/router/__snapshots__/route_component.test.ts.snap +++ b/tests/router/__snapshots__/route_component.test.ts.snap @@ -35,10 +35,11 @@ exports[`RouteComponent can render simple cases 1`] = ` let def2 = w3.__prepare(extra.fiber, undefined, undefined, sibling); 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();}}}); const fiber = w3.__owl__.currentFiber; - def2.then(function () { if (fiber.isCompleted) { w3.destroy(); 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(vn6, pvnode); w3.__owl__.pvnode = pvnode; } + w3.__owl__.parentLastFiberId = extra.fiber.id; sibling = w3.__owl__.currentFiber || sibling; } return result;