odoo.define('web.OwlCompatibilityTests', function (require) {
"use strict";
const { ComponentAdapter, ComponentWrapper, WidgetAdapterMixin } = require('web.OwlCompatibility');
const testUtils = require('web.test_utils');
const Widget = require('web.Widget');
const makeTestPromise = testUtils.makeTestPromise;
const nextTick = testUtils.nextTick;
const addMockEnvironmentOwl = testUtils.mock.addMockEnvironmentOwl;
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() {
this._super(...arguments);
WidgetAdapterMixin.destroy.call(this, ...arguments);
},
});
QUnit.module("Owl Compatibility", function () {
QUnit.module("ComponentAdapter");
QUnit.test("sub widget with no argument", async function (assert) {
assert.expect(1);
const MyWidget = Widget.extend({
start: function () {
this.$el.text('Hello World!');
}
});
class Parent extends Component {
constructor() {
super(...arguments);
this.MyWidget = MyWidget;
}
}
Parent.template = xml`
`;
Parent.components = { ComponentAdapter };
const target = testUtils.prepareTarget();
const parent = new Parent();
await parent.mount(target);
assert.strictEqual(parent.el.innerHTML, 'Hello World!
');
parent.destroy();
});
QUnit.test("sub widget with one argument", async function (assert) {
assert.expect(1);
const MyWidget = Widget.extend({
init: function (parent, name) {
this._super.apply(this, arguments);
this.name = name;
},
start: function () {
this.$el.text(`Hello ${this.name}!`);
}
});
class Parent extends Component {
constructor() {
super(...arguments);
this.MyWidget = MyWidget;
}
}
Parent.template = xml`
`;
Parent.components = { ComponentAdapter };
const target = testUtils.prepareTarget();
const parent = new Parent();
await parent.mount(target);
assert.strictEqual(parent.el.innerHTML, 'Hello World!
');
parent.destroy();
});
QUnit.test("sub widget with several arguments (common Adapter)", async function (assert) {
assert.expect(1);
const MyWidget = Widget.extend({
init: function (parent, a1, a2) {
this._super.apply(this, arguments);
this.a1 = a1;
this.a2 = a2;
},
start: function () {
this.$el.text(`${this.a1} ${this.a2}!`);
}
});
class Parent extends Component {
constructor() {
super(...arguments);
this.MyWidget = MyWidget;
}
}
Parent.template = xml`
`;
Parent.components = { ComponentAdapter };
const target = testUtils.prepareTarget();
const parent = new Parent();
try {
await parent.mount(target);
} catch (e) {
assert.strictEqual(e.toString(),
`Error: ComponentAdapter has more than 1 argument, 'widgetArgs' must be overriden.`);
}
parent.destroy();
});
QUnit.test("sub widget with several arguments (specific Adapter)", async function (assert) {
assert.expect(1);
const MyWidget = Widget.extend({
init: function (parent, a1, a2) {
this._super.apply(this, arguments);
this.a1 = a1;
this.a2 = a2;
},
start: function () {
this.$el.text(`${this.a1} ${this.a2}!`);
}
});
class MyWidgetAdapter extends ComponentAdapter {
get widgetArgs() {
return [this.props.a1, this.props.a2];
}
}
class Parent extends Component {
constructor() {
super(...arguments);
this.MyWidget = MyWidget;
}
}
Parent.template = xml`
`;
Parent.components = { MyWidgetAdapter };
const target = testUtils.prepareTarget();
const parent = new Parent();
await parent.mount(target);
assert.strictEqual(parent.el.innerHTML, 'Hello World!
');
parent.destroy();
});
QUnit.test("sub widget and widgetArgs props", async function (assert) {
assert.expect(1);
const MyWidget = Widget.extend({
init: function (parent, a1, a2) {
this._super.apply(this, arguments);
this.a1 = a1;
this.a2 = a2;
},
start: function () {
this.$el.text(`${this.a1} ${this.a2}!`);
}
});
class Parent extends Component {
constructor() {
super(...arguments);
this.MyWidget = MyWidget;
}
}
Parent.template = xml`
`;
Parent.components = { ComponentAdapter };
const target = testUtils.prepareTarget();
const parent = new Parent();
await parent.mount(target);
assert.strictEqual(parent.el.innerHTML, 'Hello World!
');
parent.destroy();
});
QUnit.test("sub widget is updated when props change", async function (assert) {
assert.expect(2);
const MyWidget = Widget.extend({
init: function (parent, name) {
this._super.apply(this, arguments);
this.name = name;
},
start: function () {
this.render();
},
render: function () {
this.$el.text(`Hello ${this.name}!`);
},
update: function (name) {
this.name = name;
},
});
class MyWidgetAdapter extends ComponentAdapter {
updateWidget(nextProps) {
return this.widget.update(nextProps.name);
}
renderWidget() {
this.widget.render();
}
}
class Parent extends Component {
constructor() {
super(...arguments);
this.MyWidget = MyWidget;
this.state = useState({
name: "World",
});
}
}
Parent.template = xml`
`;
Parent.components = { MyWidgetAdapter };
const target = testUtils.prepareTarget();
const parent = new Parent();
await parent.mount(target);
assert.strictEqual(parent.el.innerHTML, 'Hello World!
');
parent.state.name = "GED";
await nextTick();
assert.strictEqual(parent.el.innerHTML, 'Hello GED!
');
parent.destroy();
});
QUnit.test("sub widget is updated when props change (async)", async function (assert) {
assert.expect(7);
const prom = makeTestPromise();
const MyWidget = Widget.extend({
init: function (parent, name) {
this._super.apply(this, arguments);
this.name = name;
},
start: function () {
this.render();
},
render: function () {
this.$el.text(`Hello ${this.name}!`);
assert.step('render');
},
update: function (name) {
assert.step('update');
this.name = name;
},
});
class MyWidgetAdapter extends ComponentAdapter {
updateWidget(nextProps) {
return this.widget.update(nextProps.name);
}
renderWidget() {
this.widget.render();
}
}
class AsyncComponent extends Component {
willUpdateProps() {
return prom;
}
}
AsyncComponent.template = xml`Hi !
`;
class Parent extends Component {
constructor() {
super(...arguments);
this.MyWidget = MyWidget;
this.state = useState({
name: "World",
});
}
}
Parent.template = xml`
`;
Parent.components = { AsyncComponent, MyWidgetAdapter };
const target = testUtils.prepareTarget();
const parent = new Parent();
await parent.mount(target);
assert.strictEqual(parent.el.innerHTML, 'Hi World!
Hello World!
');
parent.state.name = "GED";
await nextTick();
assert.strictEqual(parent.el.innerHTML, 'Hi World!
Hello World!
');
prom.resolve();
await nextTick();
assert.strictEqual(parent.el.innerHTML, 'Hi GED!
Hello GED!
');
assert.verifySteps(['render', 'update', 'render']);
parent.destroy();
});
QUnit.test("sub widget methods are correctly called", async function (assert) {
assert.expect(8);
const MyWidget = Widget.extend({
on_attach_callback: function () {
assert.step('on_attach_callback');
},
on_detach_callback: function () {
assert.step('on_detach_callback');
},
destroy: function () {
assert.step('destroy');
this._super.apply(this, arguments);
},
});
class Parent extends Component {
constructor() {
super(...arguments);
this.MyWidget = MyWidget;
}
}
Parent.template = xml`
`;
Parent.components = { ComponentAdapter };
const target = testUtils.prepareTarget();
const parent = new Parent();
await parent.mount(target);
assert.verifySteps(['on_attach_callback']);
parent.unmount();
await parent.mount(target);
assert.verifySteps(['on_detach_callback', 'on_attach_callback']);
parent.destroy();
assert.verifySteps(['on_detach_callback', 'destroy']);
});
QUnit.test("dynamic sub widget/component", async function (assert) {
assert.expect(1);
const MyWidget = Widget.extend({
start: function () {
this.$el.text('widget');
},
});
class MyComponent extends Component {}
MyComponent.template = xml`component
`;
class Parent extends Component {
constructor() {
super(...arguments);
this.Children = [MyWidget, MyComponent];
}
}
Parent.template = xml`
`;
Parent.components = { ComponentAdapter };
const target = testUtils.prepareTarget();
const parent = new Parent();
await parent.mount(target);
assert.strictEqual(parent.el.innerHTML, 'widget
component
');
parent.destroy();
});
QUnit.test("sub widget that triggers events", async function (assert) {
assert.expect(5);
let widget;
const MyWidget = Widget.extend({
init: function () {
this._super.apply(this, arguments);
widget = this;
},
});
class Parent extends Component {
constructor() {
super(...arguments);
this.MyWidget = MyWidget;
}
onSomeEvent(ev) {
assert.step(ev.detail.value);
assert.ok(ev.detail.__targetWidget instanceof MyWidget);
}
}
Parent.template = xml`
`;
Parent.components = { ComponentAdapter };
const target = testUtils.prepareTarget();
const parent = new Parent();
await parent.mount(target);
widget.trigger_up('some-event', { value: 'a' });
widget.trigger_up('some_event', { value: 'b' }); // _ are converted to -
assert.verifySteps(['a', 'b']);
parent.destroy();
});
QUnit.test("sub widget that calls _rpc", async function (assert) {
assert.expect(3);
const MyWidget = Widget.extend({
willStart: function () {
return this._rpc({ route: 'some/route', params: { val: 2 } });
},
});
class Parent extends Component {
constructor() {
super(...arguments);
this.MyWidget = MyWidget;
}
}
Parent.template = xml`
`;
Parent.components = { ComponentAdapter };
const cleanUp = await addMockEnvironmentOwl(Parent, {
mockRPC: function (route, args) {
assert.step(`${route} ${args.val}`);
return Promise.resolve();
},
});
const target = testUtils.prepareTarget();
const parent = new Parent();
await parent.mount(target);
assert.strictEqual(parent.el.innerHTML, '');
assert.verifySteps(['some/route 2']);
parent.destroy();
cleanUp();
});
QUnit.test("sub widget that calls a service", async function (assert) {
assert.expect(1);
const MyWidget = Widget.extend({
start: function () {
let result;
this.trigger_up('call_service', {
service: 'math',
method: 'sqrt',
args: [9],
callback: r => {
result = r;
},
});
assert.strictEqual(result, 3);
},
});
class Parent extends Component {
constructor() {
super(...arguments);
this.MyWidget = MyWidget;
}
}
Parent.template = xml`
`;
Parent.components = { ComponentAdapter };
Parent.env.services.math = {
sqrt: v => Math.sqrt(v),
};
const target = testUtils.prepareTarget();
const parent = new Parent();
await parent.mount(target);
parent.destroy();
});
QUnit.test("sub widget that requests the session", async function (assert) {
assert.expect(1);
const MyWidget = Widget.extend({
start: function () {
assert.strictEqual(this.getSession().key, 'value');
},
});
class Parent extends Component {
constructor() {
super(...arguments);
this.MyWidget = MyWidget;
}
}
Parent.template = xml`
`;
Parent.components = { ComponentAdapter };
const cleanUp = await addMockEnvironmentOwl(Parent, {
session: { key: 'value' },
});
const target = testUtils.prepareTarget();
const parent = new Parent();
await parent.mount(target);
parent.destroy();
cleanUp();
});
QUnit.test("sub widget that calls load_views", async function (assert) {
assert.expect(4);
const MyWidget = Widget.extend({
willStart: function () {
return this.loadViews('some_model', { x: 2 }, [[false, 'list']]);
},
});
class Parent extends Component {
constructor() {
super(...arguments);
this.MyWidget = MyWidget;
}
}
Parent.template = xml`
`;
Parent.components = { ComponentAdapter };
const cleanUp = await addMockEnvironmentOwl(Parent, {
mockRPC: function (route, args) {
assert.strictEqual(route, '/web/dataset/call_kw/some_model');
assert.deepEqual(args.kwargs.context, { x: 2 });
assert.deepEqual(args.kwargs.views, [[false, 'list']]);
return Promise.resolve();
},
});
const target = testUtils.prepareTarget();
const parent = new Parent();
await parent.mount(target);
assert.strictEqual(parent.el.innerHTML, '');
parent.destroy();
cleanUp();
});
QUnit.test("sub widgets in a t-if/t-else", async function (assert) {
assert.expect(3);
const MyWidget1 = Widget.extend({
start: function () {
this.$el.text('Hi');
},
});
const MyWidget2 = Widget.extend({
start: function () {
this.$el.text('Hello');
},
});
class Parent extends Component {
constructor() {
super(...arguments);
this.MyWidget1 = MyWidget1;
this.MyWidget2 = MyWidget2;
this.state = useState({
flag: true,
});
}
}
Parent.template = xml`
`;
Parent.components = { ComponentAdapter };
const target = testUtils.prepareTarget();
const parent = new Parent();
await parent.mount(target);
assert.strictEqual(parent.el.innerHTML, 'Hi
');
parent.state.flag = false;
await nextTick();
assert.strictEqual(parent.el.innerHTML, 'Hello
');
parent.state.flag = true;
await nextTick();
assert.strictEqual(parent.el.innerHTML, 'Hi
');
parent.destroy();
});
QUnit.test("sub widget in a t-if, and events", async function (assert) {
assert.expect(6);
let myWidget;
const MyWidget = Widget.extend({
start: function () {
myWidget = this;
this.$el.text('Hi');
},
});
class Parent extends Component {
constructor() {
super(...arguments);
this.MyWidget = MyWidget;
this.state = useState({
flag: true,
});
}
onSomeEvent(ev) {
assert.step(ev.detail.value);
}
}
Parent.template = xml`
`;
Parent.components = { ComponentAdapter };
const target = testUtils.prepareTarget();
const parent = new Parent();
await parent.mount(target);
assert.strictEqual(parent.el.innerHTML, 'Hi
');
myWidget.trigger_up('some-event', { value: 'a' });
parent.state.flag = false;
await nextTick();
assert.strictEqual(parent.el.innerHTML, '');
myWidget.trigger_up('some-event', { value: 'b' });
parent.state.flag = true;
await nextTick();
assert.strictEqual(parent.el.innerHTML, 'Hi
');
myWidget.trigger_up('some-event', { value: 'c' });
assert.verifySteps(['a', 'c']);
parent.destroy();
});
QUnit.test("adapter keeps same el as sub widget (modify)", async function (assert) {
assert.expect(7);
let myWidget;
const MyWidget = Widget.extend({
events: {
click: "_onClick",
},
init: function (parent, name) {
myWidget = this;
this._super.apply(this, arguments);
this.name = name;
},
start: function () {
this.render();
},
render: function () {
this.$el.text("Click me!");
},
update: function (name) {
this.name = name;
},
_onClick: function () {
assert.step(this.name);
},
});
class MyWidgetAdapter extends ComponentAdapter {
updateWidget(nextProps) {
return this.widget.update(nextProps.name);
}
renderWidget() {
this.widget.render();
}
}
class Parent extends Component {
constructor() {
super(...arguments);
this.MyWidget = MyWidget;
this.state = useState({
name: "GED",
});
}
}
Parent.template = xml`
`;
Parent.components = { MyWidgetAdapter };
const target = testUtils.prepareTarget();
const parent = new Parent();
await parent.mount(target);
assert.strictEqual(parent.el, myWidget.el);
await testUtils.dom.click(parent.el);
parent.state.name = "AAB";
await nextTick();
assert.strictEqual(parent.el, myWidget.el);
await testUtils.dom.click(parent.el);
parent.state.name = "MCM";
await nextTick();
assert.strictEqual(parent.el, myWidget.el);
await testUtils.dom.click(parent.el);
assert.verifySteps(["GED", "AAB", "MCM"]);
parent.destroy();
});
QUnit.test("adapter keeps same el as sub widget (replace)", async function (assert) {
assert.expect(7);
let myWidget;
const MyWidget = Widget.extend({
events: {
click: "_onClick",
},
init: function (parent, name) {
myWidget = this;
this._super.apply(this, arguments);
this.name = name;
},
start: function () {
this.render();
},
render: function () {
this._replaceElement("Click me!
");
},
update: function (name) {
this.name = name;
},
_onClick: function () {
assert.step(this.name);
},
});
class MyWidgetAdapter extends ComponentAdapter {
updateWidget(nextProps) {
return this.widget.update(nextProps.name);
}
renderWidget() {
this.widget.render();
}
}
class Parent extends Component {
constructor() {
super(...arguments);
this.MyWidget = MyWidget;
this.state = useState({
name: "GED",
});
}
}
Parent.template = xml`
`;
Parent.components = { MyWidgetAdapter };
const target = testUtils.prepareTarget();
const parent = new Parent();
await parent.mount(target);
assert.strictEqual(parent.el, myWidget.el);
await testUtils.dom.click(parent.el);
parent.state.name = "AAB";
await nextTick();
assert.strictEqual(parent.el, myWidget.el);
await testUtils.dom.click(parent.el);
parent.state.name = "MCM";
await nextTick();
assert.strictEqual(parent.el, myWidget.el);
await testUtils.dom.click(parent.el);
assert.verifySteps(["GED", "AAB", "MCM"]);
parent.destroy();
});
QUnit.module('WidgetAdapterMixin and ComponentWrapper');
QUnit.test("widget with sub component", async function (assert) {
assert.expect(1);
class MyComponent extends Component {}
MyComponent.template = xml`Component
`;
const MyWidget = WidgetAdapter.extend({
start() {
const component = new ComponentWrapper(this, MyComponent, {});
return component.mount(this.el);
}
});
const target = testUtils.prepareTarget();
const widget = new MyWidget();
await widget.appendTo(target);
assert.strictEqual(widget.el.innerHTML, 'Component
');
widget.destroy();
});
QUnit.test("sub component hooks are correctly called", async function (assert) {
assert.expect(14);
let component;
class MyComponent extends Component {
constructor(parent) {
super(parent);
assert.step("init");
}
async willStart() {
assert.step("willStart");
}
mounted() {
assert.step("mounted");
}
willUnmount() {
assert.step("willUnmount");
}
__destroy() {
super.__destroy();
assert.step("__destroy");
}
}
MyComponent.template = xml`Component
`;
const MyWidget = WidgetAdapter.extend({
start() {
component = new ComponentWrapper(this, MyComponent, {});
return component.mount(this.el);
}
});
const target = testUtils.prepareTarget();
const widget = new MyWidget();
await widget.appendTo(target);
assert.verifySteps(['init', 'willStart', 'mounted']);
assert.ok(component.__owl__.status === ISMOUNTED);
widget.$el.detach();
widget.on_detach_callback();
assert.verifySteps(['willUnmount']);
assert.ok(component.__owl__.status !== ISMOUNTED);
widget.$el.appendTo(target);
widget.on_attach_callback();
assert.verifySteps(['mounted']);
assert.ok(component.__owl__.status === ISMOUNTED);
widget.destroy();
assert.verifySteps(['willUnmount', '__destroy']);
});
QUnit.test("isMounted with several sub components", async function (assert) {
assert.expect(9);
let c1;
let c2;
class MyComponent extends Component {}
MyComponent.template = xml`Component
`;
const MyWidget = WidgetAdapter.extend({
start() {
c1 = new ComponentWrapper(this, MyComponent, {id: 1});
c2 = new ComponentWrapper(this, MyComponent, {id: 2});
return Promise.all([c1.mount(this.el), c2.mount(this.el)]);
}
});
const target = testUtils.prepareTarget();
const widget = new MyWidget();
await widget.appendTo(target);
assert.strictEqual(widget.el.innerHTML, 'Component 1
Component 2
');
assert.ok(c1.__owl__.status === ISMOUNTED);
assert.ok(c2.__owl__.status === ISMOUNTED);
widget.$el.detach();
widget.on_detach_callback();
assert.ok(c1.__owl__.status !== ISMOUNTED);
assert.ok(c2.__owl__.status !== ISMOUNTED);
widget.$el.appendTo(target);
widget.on_attach_callback();
assert.ok(c1.__owl__.status === ISMOUNTED);
assert.ok(c2.__owl__.status === ISMOUNTED);
widget.destroy();
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(5);
let child;
class MyChildComponent extends Component {
constructor() {
super(...arguments);
child = this;
}
}
MyChildComponent.template = xml`child
`;
class MyComponent extends Component {}
MyComponent.template = xml`
`;
MyComponent.components = { MyChildComponent };
const MyWidget = WidgetAdapter.extend({
start() {
let component = new ComponentWrapper(this, MyComponent, {});
return component.mount(this.el);
}
});
const target = testUtils.prepareTarget();
const widget = new MyWidget();
await widget.appendTo(target);
assert.strictEqual(widget.el.innerHTML, '');
assert.ok(child.__owl__.status === ISMOUNTED);
widget.$el.detach();
widget.on_detach_callback();
assert.ok(child.__owl__.status !== ISMOUNTED);
widget.$el.appendTo(target);
widget.on_attach_callback();
assert.ok(child.__owl__.status === ISMOUNTED);
widget.destroy();
assert.ok(child.__owl__.status === ISDESTROYED);
});
QUnit.test("sub component can be updated (in DOM)", async function (assert) {
assert.expect(2);
class MyComponent extends Component {}
MyComponent.template = xml`Component
`;
const MyWidget = WidgetAdapter.extend({
start() {
this.component = new ComponentWrapper(this, MyComponent, {val: 1});
return this.component.mount(this.el);
},
update() {
return this.component.update({val: 2});
},
});
const target = testUtils.prepareTarget();
const widget = new MyWidget();
await widget.appendTo(target);
assert.strictEqual(widget.el.innerHTML, 'Component 1
');
await widget.update();
assert.strictEqual(widget.el.innerHTML, 'Component 2
');
widget.destroy();
});
QUnit.test("sub component can be updated (not in DOM)", async function (assert) {
assert.expect(4);
class MyComponent extends Component {}
MyComponent.template = xml`Component
`;
const MyWidget = WidgetAdapter.extend({
start() {
this.component = new ComponentWrapper(this, MyComponent, {val: 1});
return this.component.mount(this.el);
},
update() {
return this.component.update({val: 2});
},
});
const target = testUtils.prepareTarget();
const widget = new MyWidget();
await widget.appendTo(target);
assert.strictEqual(widget.el.innerHTML, 'Component 1
');
widget.$el.detach();
widget.on_detach_callback();
assert.ok(widget.component.__owl__.status !== ISMOUNTED);
await widget.update();
widget.$el.appendTo(target);
widget.on_attach_callback();
assert.ok(widget.component.__owl__.status === ISMOUNTED);
assert.strictEqual(widget.el.innerHTML, 'Component 2
');
widget.destroy();
});
QUnit.test("update a destroyed sub component", async function (assert) {
assert.expect(1);
class MyComponent extends Component {}
MyComponent.template = xml`Component
`;
const MyWidget = WidgetAdapter.extend({
start() {
this.component = new ComponentWrapper(this, MyComponent, {val: 1});
return this.component.mount(this.el);
},
update() {
this.component.update({val: 2});
},
});
const target = testUtils.prepareTarget();
const widget = new MyWidget();
await widget.appendTo(target);
assert.strictEqual(widget.el.innerHTML, 'Component 1
');
widget.destroy();
widget.update(); // should not crash
});
QUnit.test("sub component that triggers events", async function (assert) {
assert.expect(3);
class WidgetComponent extends Component {}
WidgetComponent.template = xml`Component
`;
const MyWidget = WidgetAdapter.extend({
custom_events: _.extend({}, Widget.custom_events, {
some_event: function (ev) {
assert.step(ev.data.value);
}
}),
start() {
this.component = new ComponentWrapper(this, WidgetComponent, {});
return this.component.mount(this.el);
},
});
const target = testUtils.prepareTarget();
const widget = new MyWidget();
await widget.appendTo(target);
widget.component.trigger('some_event', { value: 'a' });
widget.component.trigger('some-event', { value: 'b' }); // - are converted to _
assert.verifySteps(['a', 'b']);
widget.destroy();
});
QUnit.test("change parent of ComponentWrapper", async function (assert) {
assert.expect(7);
let myComponent;
let widget1;
let widget2;
class WidgetComponent extends Component {}
WidgetComponent.template = xml`Component
`;
const MyWidget = WidgetAdapter.extend({
custom_events: _.extend({}, Widget.custom_events, {
some_event: function (ev) {
assert.strictEqual(this, ev.data.widget);
assert.step(ev.data.value);
}
}),
});
const Parent = Widget.extend({
start() {
const proms = [];
myComponent = new ComponentWrapper(null, WidgetComponent, {});
widget1 = new MyWidget();
widget2 = new MyWidget();
proms.push(myComponent.mount(this.el));
proms.push(widget1.appendTo(this.$el));
proms.push(widget2.appendTo(this.$el));
return Promise.all(proms);
}
});
const target = testUtils.prepareTarget();
const parent = new Parent();
await parent.appendTo(target);
// 1. No parent
myComponent.trigger('some-event', { value: 'a', widget: null });
assert.verifySteps([]);
// 2. No parent --> parent (widget1)
myComponent.unmount();
await myComponent.mount(widget1.el);
myComponent.setParent(widget1);
myComponent.trigger('some-event', { value: 'b', widget: widget1 });
assert.verifySteps(['b']);
// 3. Parent (widget1) --> new parent (widget2)
myComponent.unmount();
await myComponent.mount(widget2.el);
myComponent.setParent(widget2);
myComponent.trigger('some-event', { value: 'c', widget: widget2 });
assert.verifySteps(['c']);
parent.destroy();
});
QUnit.module('Several layers of legacy widgets and Owl components');
QUnit.test("Owl over legacy over Owl", async function (assert) {
assert.expect(7);
let leafComponent;
class MyComponent extends Component {}
MyComponent.template = xml`Component`;
const MyWidget = WidgetAdapter.extend({
custom_events: {
widget_event: function (ev) {
assert.step(`[widget] widget-event ${ev.data.value}`);
},
both_event: function (ev) {
assert.step(`[widget] both-event ${ev.data.value}`);
if (ev.data.value === 4) {
ev.stopPropagation();
}
}
},
start() {
leafComponent = new ComponentWrapper(this, MyComponent, {});
return leafComponent.mount(this.el);
},
});
class Parent extends Component {
constructor() {
super(...arguments);
this.MyWidget = MyWidget;
}
onRootEvent(ev) {
assert.step(`[root] root-event ${ev.detail.value}`);
}
onBothEvent(ev) {
assert.step(`[root] both-event ${ev.detail.value}`);
}
}
Parent.template = xml`
`;
Parent.components = { ComponentAdapter };
const target = testUtils.prepareTarget();
const parent = new Parent();
await parent.mount(target);
assert.strictEqual(parent.el.innerHTML, 'Component
');
leafComponent.trigger('root-event', { value: 1 });
leafComponent.trigger('widget-event', { value: 2 });
leafComponent.trigger('both-event', { value: 3 });
leafComponent.trigger('both-event', { value: 4 }); // will be stopped by widget
assert.verifySteps([
'[root] root-event 1',
'[widget] widget-event 2',
'[widget] both-event 3',
'[root] both-event 3',
'[widget] both-event 4',
]);
parent.destroy();
});
QUnit.test("Legacy over Owl over legacy", async function (assert) {
assert.expect(7);
let leafWidget;
const MyWidget = Widget.extend({
start: function () {
leafWidget = this;
this.$el.text('Widget');
}
});
class MyComponent extends Component {
constructor() {
super(...arguments);
this.MyWidget = MyWidget;
}
onComponentEvent(ev) {
assert.step(`[component] component-event ${ev.detail.value}`);
}
onBothEvent(ev) {
assert.step(`[component] both-event ${ev.detail.value}`);
if (ev.detail.value === 4) {
ev.stopPropagation();
}
}
}
MyComponent.template = xml`
`;
MyComponent.components = { ComponentAdapter };
const Parent = WidgetAdapter.extend({
custom_events: {
root_event: function (ev) {
assert.step(`[root] root-event ${ev.data.value}`);
},
both_event: function (ev) {
assert.step(`[root] both-event ${ev.data.value}`);
},
},
start() {
const component = new ComponentWrapper(this, MyComponent, {});
return component.mount(this.el);
}
});
const target = testUtils.prepareTarget();
const parent = new Parent();
await parent.appendTo(target);
assert.strictEqual(parent.el.innerHTML, 'Widget
');
leafWidget.trigger_up('root-event', { value: 1 });
leafWidget.trigger_up('component-event', { value: 2 });
leafWidget.trigger_up('both-event', { value: 3 });
leafWidget.trigger_up('both-event', { value: 4 }); // will be stopped by component
assert.verifySteps([
'[root] root-event 1',
'[component] component-event 2',
'[component] both-event 3',
'[root] both-event 3',
'[component] both-event 4',
]);
parent.destroy();
});
});
});