summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--addons/point_of_sale/static/src/js/Gui.js2
-rw-r--r--addons/web/static/lib/owl/owl.js207
-rw-r--r--addons/web/static/src/js/component_extension.js4
-rw-r--r--addons/web/static/src/js/owl_compatibility.js4
-rw-r--r--addons/web/static/src/js/tools/test_menus.js17
-rw-r--r--addons/web/static/tests/owl_compatibility_tests.js44
6 files changed, 155 insertions, 123 deletions
diff --git a/addons/point_of_sale/static/src/js/Gui.js b/addons/point_of_sale/static/src/js/Gui.js
index 7cc795f24fd..0720b397361 100644
--- a/addons/point_of_sale/static/src/js/Gui.js
+++ b/addons/point_of_sale/static/src/js/Gui.js
@@ -49,7 +49,7 @@ odoo.define('point_of_sale.Gui', function (require) {
get(target, key) {
const { component, availableMethods } = target;
if (!component) throw new Error(`Call 'configureGui' before using Gui.`);
- const isMounted = component.__owl__.isMounted;
+ const isMounted = component.__owl__.status === 3 /* mounted */;
if (availableMethods.has(key) && isMounted) {
return component[key].bind(component);
}
diff --git a/addons/web/static/lib/owl/owl.js b/addons/web/static/lib/owl/owl.js
index d35e8e97484..c1cb28a4670 100644
--- a/addons/web/static/lib/owl/owl.js
+++ b/addons/web/static/lib/owl/owl.js
@@ -181,15 +181,15 @@
// Misc types, constants and helpers
//------------------------------------------------------------------------------
const RESERVED_WORDS = "true,false,NaN,null,undefined,debugger,console,window,in,instanceof,new,function,return,this,eval,void,Math,RegExp,Array,Object,Date".split(",");
- const WORD_REPLACEMENT = {
+ const WORD_REPLACEMENT = Object.assign(Object.create(null), {
and: "&&",
or: "||",
gt: ">",
gte: ">=",
lt: "<",
lte: "<=",
- };
- const STATIC_TOKEN_MAP = {
+ });
+ const STATIC_TOKEN_MAP = Object.assign(Object.create(null), {
"{": "LEFT_BRACE",
"}": "RIGHT_BRACE",
"[": "LEFT_BRACKET",
@@ -198,7 +198,7 @@
",": "COMMA",
"(": "LEFT_PAREN",
")": "RIGHT_PAREN",
- };
+ });
// note that the space after typeof is relevant. It makes sure that the formatted
// expression has a space after typeof
const OPERATORS = "...,.,===,==,+,!==,!=,!,||,&&,>=,>,<=,<,?,-,*,/,%,typeof ,=>,=,;,in ".split(",");
@@ -2531,7 +2531,7 @@
code = ctx.captureExpression(value);
}
const modCode = mods.map((mod) => modcodes[mod]).join("");
- let handler = `function (e) {if (!context.__owl__.isMounted){return}${modCode}${code}}`;
+ let handler = `function (e) {if (context.__owl__.status === ${5 /* DESTROYED */}){return}${modCode}${code}}`;
if (putInCache) {
const key = ctx.generateTemplateKey(event);
ctx.addLine(`extra.handlers[${key}] = extra.handlers[${key}] || ${handler};`);
@@ -3176,7 +3176,7 @@
// need to update component
let styleCode = "";
if (tattStyle) {
- styleCode = `.then(()=>{if (w${componentID}.__owl__.isDestroyed) {return};w${componentID}.el.style=${tattStyle};});`;
+ styleCode = `.then(()=>{if (w${componentID}.__owl__.status === ${5 /* DESTROYED */}) {return};w${componentID}.el.style=${tattStyle};});`;
}
ctx.addLine(`w${componentID}.__updateProps(props${componentID}, extra.fiber, ${scope})${styleCode};`);
ctx.addLine(`let pvnode = w${componentID}.__owl__.pvnode;`);
@@ -3440,6 +3440,8 @@
*/
_reuseFiber(oldFiber) {
oldFiber.cancel(); // cancel children fibers
+ oldFiber.target = this.target || oldFiber.target;
+ oldFiber.position = this.position || oldFiber.position;
oldFiber.isCompleted = false; // keep the root fiber alive
oldFiber.isRendered = false; // the fiber has to be re-rendered
if (oldFiber.child) {
@@ -3519,8 +3521,8 @@
complete() {
let component = this.component;
this.isCompleted = true;
- const { isMounted, isDestroyed } = component.__owl__;
- if (isDestroyed) {
+ const status = component.__owl__.status;
+ if (status === 5 /* DESTROYED */) {
return;
}
// build patchQueue
@@ -3532,7 +3534,7 @@
this._walk(doWork);
const patchLen = patchQueue.length;
// call willPatch hook on each fiber of patchQueue
- if (isMounted) {
+ if (status === 3 /* MOUNTED */) {
for (let i = 0; i < patchLen; i++) {
const fiber = patchQueue[i];
if (fiber.shouldPatch) {
@@ -3582,8 +3584,9 @@
component.__owl__.pvnode.elm = component.__owl__.vnode.elm;
}
}
- if (fiber === component.__owl__.currentFiber) {
- component.__owl__.currentFiber = null;
+ const compOwl = component.__owl__;
+ if (fiber === compOwl.currentFiber) {
+ compOwl.currentFiber = null;
}
}
// insert into the DOM (mount case)
@@ -3601,7 +3604,7 @@
this.component.env.qweb.trigger("dom-appended");
}
// call patched/mounted hook on each fiber of (reversed) patchQueue
- if (isMounted || inDOM) {
+ if (status === 3 /* MOUNTED */ || inDOM) {
for (let i = patchLen - 1; i >= 0; i--) {
const fiber = patchQueue[i];
component = fiber.component;
@@ -3616,6 +3619,13 @@
}
}
}
+ else {
+ for (let i = patchLen - 1; i >= 0; i--) {
+ const fiber = patchQueue[i];
+ component = fiber.component;
+ component.__owl__.status = 4 /* UNMOUNTED */;
+ }
+ }
}
/**
* Cancel a fiber and all its children.
@@ -3845,6 +3855,15 @@
document.head.appendChild(sheet);
}
+ var STATUS;
+ (function (STATUS) {
+ STATUS[STATUS["CREATED"] = 0] = "CREATED";
+ STATUS[STATUS["WILLSTARTED"] = 1] = "WILLSTARTED";
+ STATUS[STATUS["RENDERED"] = 2] = "RENDERED";
+ STATUS[STATUS["MOUNTED"] = 3] = "MOUNTED";
+ STATUS[STATUS["UNMOUNTED"] = 4] = "UNMOUNTED";
+ STATUS[STATUS["DESTROYED"] = 5] = "DESTROYED";
+ })(STATUS || (STATUS = {}));
const portalSymbol = Symbol("portal"); // FIXME
//------------------------------------------------------------------------------
// Component
@@ -3892,16 +3911,18 @@
this.env.browser = browser;
}
this.env.qweb.on("update", this, () => {
- if (this.__owl__.isMounted) {
- this.render(true);
- }
- if (this.__owl__.isDestroyed) {
- // this is unlikely to happen, but if a root widget is destroyed,
- // we want to remove our subscription. The usual way to do that
- // would be to perform some check in the destroy method, but since
- // it is very performance sensitive, and since this is a rare event,
- // we simply do it lazily
- this.env.qweb.off("update", this);
+ switch (this.__owl__.status) {
+ case 3 /* MOUNTED */:
+ this.render(true);
+ break;
+ case 5 /* DESTROYED */:
+ // this is unlikely to happen, but if a root widget is destroyed,
+ // we want to remove our subscription. The usual way to do that
+ // would be to perform some check in the destroy method, but since
+ // it is very performance sensitive, and since this is a rare event,
+ // we simply do it lazily
+ this.env.qweb.off("update", this);
+ break;
}
});
depth = 0;
@@ -3913,8 +3934,7 @@
depth: depth,
vnode: null,
pvnode: null,
- isMounted: false,
- isDestroyed: false,
+ status: 0 /* CREATED */,
parent: parent || null,
children: {},
cmap: {},
@@ -3936,6 +3956,7 @@
if (constr.style) {
this.__applyStyles(constr);
}
+ this.setup();
}
/**
* The `el` is the root element of the component. Note that it could be null:
@@ -3945,6 +3966,16 @@
return this.__owl__.vnode ? this.__owl__.vnode.elm : null;
}
/**
+ * setup is run just after the component is constructed. This is the standard
+ * location where the component can setup its hooks. It has some advantages
+ * over the constructor:
+ * - it can be patched (useful in odoo ecosystem)
+ * - it does not need to propagate the arguments to the super call
+ *
+ * Note: this method should not be called manually.
+ */
+ setup() { }
+ /**
* willStart is an asynchronous hook that can be implemented to perform some
* action before the initial rendering of a component.
*
@@ -4022,60 +4053,53 @@
* Note that a component can be mounted an unmounted several times
*/
async mount(target, options = {}) {
+ if (!(target instanceof HTMLElement || target instanceof DocumentFragment)) {
+ 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 position = options.position || "last-child";
const __owl__ = this.__owl__;
- if (__owl__.isMounted) {
- if (position !== "self" && this.el.parentNode !== target) {
- // in this situation, we are trying to mount a component on a different
- // target. In this case, we need to unmount first, otherwise it will
- // not work.
- this.unmount();
- }
- else {
- return Promise.resolve();
- }
- }
- if (__owl__.isDestroyed) {
- throw new Error("Cannot mount a destroyed component");
- }
- if (__owl__.currentFiber) {
- const currentFiber = __owl__.currentFiber;
- if (!currentFiber.target && !currentFiber.position) {
- // this means we have a pending rendering, but it was a render operation,
- // not a mount operation. We can simply update the fiber with the target
- // and the position
+ const currentFiber = __owl__.currentFiber;
+ switch (__owl__.status) {
+ case 0 /* CREATED */: {
+ const fiber = new Fiber(null, this, true, target, position);
+ fiber.shouldPatch = false;
+ this.__prepareAndRender(fiber, () => { });
+ return scheduler.addFiber(fiber);
+ }
+ case 1 /* WILLSTARTED */:
+ case 2 /* RENDERED */:
currentFiber.target = target;
currentFiber.position = position;
return scheduler.addFiber(currentFiber);
+ case 4 /* UNMOUNTED */: {
+ const fiber = new Fiber(null, this, true, target, position);
+ fiber.shouldPatch = false;
+ this.__render(fiber);
+ return scheduler.addFiber(fiber);
+ }
+ case 3 /* MOUNTED */: {
+ if (position !== "self" && this.el.parentNode !== target) {
+ const fiber = new Fiber(null, this, true, target, position);
+ fiber.shouldPatch = false;
+ this.__render(fiber);
+ return scheduler.addFiber(fiber);
+ }
+ else {
+ return Promise.resolve();
+ }
}
- else if (currentFiber.target === target && currentFiber.position === position) {
- return scheduler.addFiber(currentFiber);
- }
- else {
- scheduler.rejectFiber(currentFiber, "Mounting operation cancelled");
- }
- }
- if (!(target instanceof HTMLElement || target instanceof DocumentFragment)) {
- 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, true, target, position);
- fiber.shouldPatch = false;
- if (!__owl__.vnode) {
- this.__prepareAndRender(fiber, () => { });
+ case 5 /* DESTROYED */:
+ throw new Error("Cannot mount a destroyed component");
}
- else {
- this.__render(fiber);
- }
- return scheduler.addFiber(fiber);
}
/**
* The unmount method is the opposite of the mount method. It is useful
* to call willUnmount calls and remove the component from the DOM.
*/
unmount() {
- if (this.__owl__.isMounted) {
+ if (this.__owl__.status === 3 /* MOUNTED */) {
this.__callWillUnmount();
this.el.remove();
}
@@ -4101,11 +4125,11 @@
// if we aren't mounted at this point, it implies that there is a
// currentFiber that is already rendered (isRendered is true), so we are
// about to be mounted
- const isMounted = __owl__.isMounted;
+ const status = __owl__.status;
const fiber = new Fiber(null, this, force, null, null);
Promise.resolve().then(() => {
- if (__owl__.isMounted || !isMounted) {
- if (fiber.isCompleted) {
+ if (__owl__.status === 3 /* MOUNTED */ || status !== 3 /* MOUNTED */) {
+ if (fiber.isCompleted || fiber.isRendered) {
return;
}
this.__render(fiber);
@@ -4131,7 +4155,7 @@
*/
destroy() {
const __owl__ = this.__owl__;
- if (!__owl__.isDestroyed) {
+ if (__owl__.status !== 5 /* DESTROYED */) {
const el = this.el;
this.__destroy(__owl__.parent);
if (el) {
@@ -4172,13 +4196,12 @@
*/
__destroy(parent) {
const __owl__ = this.__owl__;
- const isMounted = __owl__.isMounted;
- if (isMounted) {
+ if (__owl__.status === 3 /* MOUNTED */) {
if (__owl__.willUnmountCB) {
__owl__.willUnmountCB();
}
this.willUnmount();
- __owl__.isMounted = false;
+ __owl__.status = 4 /* UNMOUNTED */;
}
const children = __owl__.children;
for (let key in children) {
@@ -4189,7 +4212,7 @@
delete parent.__owl__.children[id];
__owl__.parent = null;
}
- __owl__.isDestroyed = true;
+ __owl__.status = 5 /* DESTROYED */;
delete __owl__.vnode;
if (__owl__.currentFiber) {
__owl__.currentFiber.isCompleted = true;
@@ -4197,7 +4220,7 @@
}
__callMounted() {
const __owl__ = this.__owl__;
- __owl__.isMounted = true;
+ __owl__.status = 3 /* MOUNTED */;
__owl__.currentFiber = null;
this.mounted();
if (__owl__.mountedCB) {
@@ -4210,7 +4233,7 @@
__owl__.willUnmountCB();
}
this.willUnmount();
- __owl__.isMounted = false;
+ __owl__.status = 4 /* UNMOUNTED */;
if (__owl__.currentFiber) {
__owl__.currentFiber.isCompleted = true;
__owl__.currentFiber.root.counter = 0;
@@ -4218,7 +4241,7 @@
const children = __owl__.children;
for (let id in children) {
const comp = children[id];
- if (comp.__owl__.isMounted) {
+ if (comp.__owl__.status === 3 /* MOUNTED */) {
comp.__callWillUnmount();
}
}
@@ -4338,17 +4361,23 @@
}
async __prepareAndRender(fiber, cb) {
try {
- await Promise.all([this.willStart(), this.__owl__.willStartCB && this.__owl__.willStartCB()]);
+ const proms = Promise.all([
+ this.willStart(),
+ this.__owl__.willStartCB && this.__owl__.willStartCB(),
+ ]);
+ this.__owl__.status = 1 /* WILLSTARTED */;
+ await proms;
+ if (this.__owl__.status === 5 /* DESTROYED */) {
+ return Promise.resolve();
+ }
}
catch (e) {
fiber.handleError(e);
return Promise.resolve();
}
- if (this.__owl__.isDestroyed) {
- return Promise.resolve();
- }
if (!fiber.isCompleted) {
this.__render(fiber);
+ this.__owl__.status = 2 /* RENDERED */;
cb();
}
}
@@ -4370,7 +4399,7 @@
for (let childKey in __owl__.children) {
const child = __owl__.children[childKey];
const childOwl = child.__owl__;
- if (!childOwl.isMounted && childOwl.parentLastFiberId < fiber.id) {
+ if (childOwl.status !== 3 /* MOUNTED */ && childOwl.parentLastFiberId < fiber.id) {
// we only do here a "soft" destroy, meaning that we leave the child
// dom node alone, without removing it. Most of the time, it does not
// matter, because the child component is already unmounted. However,
@@ -4417,16 +4446,6 @@
}
}
/**
- * Only called by qweb t-component directive (when t-keepalive is set)
- */
- __remount() {
- const __owl__ = this.__owl__;
- if (!__owl__.isMounted) {
- __owl__.isMounted = true;
- this.mounted();
- }
- }
- /**
* Apply default props (only top level).
*
* Note that this method does modify in place the props
@@ -5372,9 +5391,9 @@
exports.utils = utils;
- __info__.version = '1.2.3';
- __info__.date = '2021-01-19T14:42:29.241Z';
- __info__.hash = '490cf18';
+ __info__.version = '1.2.4';
+ __info__.date = '2021-02-10T13:24:25.187Z';
+ __info__.hash = '985e985';
__info__.url = 'https://github.com/odoo/owl';
diff --git a/addons/web/static/src/js/component_extension.js b/addons/web/static/src/js/component_extension.js
index ce9a3228569..4edaee807dd 100644
--- a/addons/web/static/src/js/component_extension.js
+++ b/addons/web/static/src/js/component_extension.js
@@ -13,12 +13,12 @@
return new Promise((resolve, reject) => {
return this.env.services.rpc(...arguments)
.then(result => {
- if (!this.__owl__.isDestroyed) {
+ if (this.__owl__.status !== 5 /* not destroyed */) {
resolve(result);
}
})
.catch(reason => {
- if (!this.__owl__.isDestroyed) {
+ if (this.__owl__.status !== 5) /* not destroyed */ {
reject(reason);
}
});
diff --git a/addons/web/static/src/js/owl_compatibility.js b/addons/web/static/src/js/owl_compatibility.js
index a33f5ebed58..b20ad7890f0 100644
--- a/addons/web/static/src/js/owl_compatibility.js
+++ b/addons/web/static/src/js/owl_compatibility.js
@@ -439,14 +439,14 @@ odoo.define('web.OwlCompatibility', function () {
* @return {Promise}
*/
async update(props = {}) {
- if (this.__owl__.isDestroyed) {
+ if (this.__owl__.status === 5 /* destroyed */) {
return new Promise(() => {});
}
Object.assign(this.props, props);
let prom;
- if (this.__owl__.isMounted) {
+ if (this.__owl__.status === 3 /* mounted */) {
prom = this.render();
} else {
// we may not be in the DOM, but actually want to be redrawn
diff --git a/addons/web/static/src/js/tools/test_menus.js b/addons/web/static/src/js/tools/test_menus.js
index 3b0523d8f65..9112b7f5961 100644
--- a/addons/web/static/src/js/tools/test_menus.js
+++ b/addons/web/static/src/js/tools/test_menus.js
@@ -106,6 +106,9 @@
console.log("Testing app menu:", element.dataset.menuXmlid);
if (testedApps.indexOf(element.dataset.menuXmlid) >= 0) return; // Another infinite loop protection
testedApps.push(element.dataset.menuXmlid);
+ if (isEnterprise) {
+ await ensureHomeMenu();
+ }
await testMenuItem(element);
if (appsMenusOnly === true) return;
const subMenuItems = document.querySelectorAll('.o_menu_entry_lvl_1, .o_menu_entry_lvl_2, .o_menu_entry_lvl_3, .o_menu_entry_lvl_4');
@@ -113,8 +116,7 @@
await testMenuItem(subMenuItem);
}
if (isEnterprise) {
- const homeMenu = document.querySelector('nav.o_main_navbar > a.o_menu_toggle.fa-th');
- return triggerClick(homeMenu, "home menu toggle button");
+ await ensureHomeMenu();
}
}
@@ -276,6 +278,17 @@
return promise;
}
+ /**
+ * Make sure the home menu is open
+ */
+ async function ensureHomeMenu() {
+ const menuToggle = document.querySelector('nav.o_main_navbar > a.o_menu_toggle.fa-th');
+ if (menuToggle) {
+ await triggerClick(menuToggle, 'home menu toggle button');
+ await waitForCondition(() => document.querySelector('.o_home_menu'));
+ }
+ }
+
const MOUSE_EVENTS = [
'mouseover',
'mouseenter',
diff --git a/addons/web/static/tests/owl_compatibility_tests.js b/addons/web/static/tests/owl_compatibility_tests.js
index a4f453867cf..7be3b17b857 100644
--- a/addons/web/static/tests/owl_compatibility_tests.js
+++ b/addons/web/static/tests/owl_compatibility_tests.js
@@ -12,6 +12,9 @@ odoo.define('web.OwlCompatibilityTests', function (require) {
const { Component, tags, useState } = owl;
const { xml } = tags;
+ // from Owl internal status enum
+ const ISMOUNTED = 3;
+ const ISDESTROYED = 5;
const WidgetAdapter = Widget.extend(WidgetAdapterMixin, {
destroy() {
@@ -883,19 +886,19 @@ odoo.define('web.OwlCompatibilityTests', function (require) {
await widget.appendTo(target);
assert.verifySteps(['init', 'willStart', 'mounted']);
- assert.ok(component.__owl__.isMounted);
+ assert.ok(component.__owl__.status === ISMOUNTED);
widget.$el.detach();
widget.on_detach_callback();
assert.verifySteps(['willUnmount']);
- assert.ok(!component.__owl__.isMounted);
+ assert.ok(component.__owl__.status !== ISMOUNTED);
widget.$el.appendTo(target);
widget.on_attach_callback();
assert.verifySteps(['mounted']);
- assert.ok(component.__owl__.isMounted);
+ assert.ok(component.__owl__.status === ISMOUNTED);
widget.destroy();
@@ -903,7 +906,7 @@ odoo.define('web.OwlCompatibilityTests', function (require) {
});
QUnit.test("isMounted with several sub components", async function (assert) {
- assert.expect(11);
+ assert.expect(9);
let c1;
let c2;
@@ -922,31 +925,29 @@ odoo.define('web.OwlCompatibilityTests', function (require) {
await widget.appendTo(target);
assert.strictEqual(widget.el.innerHTML, '<div>Component 1</div><div>Component 2</div>');
- assert.ok(c1.__owl__.isMounted);
- assert.ok(c2.__owl__.isMounted);
+ assert.ok(c1.__owl__.status === ISMOUNTED);
+ assert.ok(c2.__owl__.status === ISMOUNTED);
widget.$el.detach();
widget.on_detach_callback();
- assert.ok(!c1.__owl__.isMounted);
- assert.ok(!c2.__owl__.isMounted);
+ assert.ok(c1.__owl__.status !== ISMOUNTED);
+ assert.ok(c2.__owl__.status !== ISMOUNTED);
widget.$el.appendTo(target);
widget.on_attach_callback();
- assert.ok(c1.__owl__.isMounted);
- assert.ok(c2.__owl__.isMounted);
+ assert.ok(c1.__owl__.status === ISMOUNTED);
+ assert.ok(c2.__owl__.status === ISMOUNTED);
widget.destroy();
- assert.ok(!c1.__owl__.isMounted);
- assert.ok(!c2.__owl__.isMounted);
- assert.ok(c1.__owl__.isDestroyed);
- assert.ok(c2.__owl__.isDestroyed);
+ assert.ok(c1.__owl__.status === ISDESTROYED);
+ assert.ok(c2.__owl__.status === ISDESTROYED);
});
QUnit.test("isMounted with several levels of sub components", async function (assert) {
- assert.expect(6);
+ assert.expect(5);
let child;
class MyChildComponent extends Component {
@@ -971,22 +972,21 @@ odoo.define('web.OwlCompatibilityTests', function (require) {
await widget.appendTo(target);
assert.strictEqual(widget.el.innerHTML, '<div><div>child</div></div>');
- assert.ok(child.__owl__.isMounted);
+ assert.ok(child.__owl__.status === ISMOUNTED);
widget.$el.detach();
widget.on_detach_callback();
- assert.ok(!child.__owl__.isMounted);
+ assert.ok(child.__owl__.status !== ISMOUNTED);
widget.$el.appendTo(target);
widget.on_attach_callback();
- assert.ok(child.__owl__.isMounted);
+ assert.ok(child.__owl__.status === ISMOUNTED);
widget.destroy();
- assert.ok(!child.__owl__.isMounted);
- assert.ok(child.__owl__.isDestroyed);
+ assert.ok(child.__owl__.status === ISDESTROYED);
});
QUnit.test("sub component can be updated (in DOM)", async function (assert) {
@@ -1041,14 +1041,14 @@ odoo.define('web.OwlCompatibilityTests', function (require) {
widget.$el.detach();
widget.on_detach_callback();
- assert.ok(!widget.component.__owl__.isMounted);
+ assert.ok(widget.component.__owl__.status !== ISMOUNTED);
await widget.update();
widget.$el.appendTo(target);
widget.on_attach_callback();
- assert.ok(widget.component.__owl__.isMounted);
+ assert.ok(widget.component.__owl__.status === ISMOUNTED);
assert.strictEqual(widget.el.innerHTML, '<div>Component 2</div>');
widget.destroy();