Files
odoo_source/addons/web/static/tests/component_extension_tests.js
T
Lucas Perais (lpe) 6bdcf054fd [IMP] web: custom_hooks: dynamic inheritable event handling
This commit's intention is to bring class inheritance of event handlers
to components.
Use case:
A component class may want to set generic event handlers for all classes
inheriting from it, in order to set shared event behavior from the start
It is the case in many parts of Odoo, in particular in the cases of fields
which need at least to react on some keyboard event for navigation

The way to implement such a behavior is to make use of the
`useListener` OWL hook, by passing as arguments
event name and the handler function
2020-02-03 09:15:02 +00:00

186 lines
6.5 KiB
JavaScript

odoo.define('web.component_extension_tests', function (require) {
"use strict";
const makeTestEnvironment = require("web.test_env");
const testUtils = require("web.test_utils");
const { Component, tags } = owl;
const { xml } = tags;
const { useListener } = require('web.custom_hooks');
QUnit.module("web", function () {
QUnit.module("Component Extension");
QUnit.test("Component destroyed while performing successful RPC", async function (assert) {
assert.expect(1);
class Parent extends Component {}
Parent.env = makeTestEnvironment({}, () => Promise.resolve());
Parent.template = xml`<div/>`;
const parent = new Parent();
parent.rpc({}).then(() => { throw new Error(); });
parent.destroy();
await testUtils.nextTick();
assert.ok(true, "Promise should still be pending");
});
QUnit.test("Component destroyed while performing failed RPC", async function (assert) {
assert.expect(1);
class Parent extends Component {}
Parent.env = makeTestEnvironment({}, () => Promise.reject());
Parent.template = xml`<div/>`;
const parent = new Parent();
parent.rpc({}).catch(() => { throw new Error(); });
parent.destroy();
await testUtils.nextTick();
assert.ok(true, "Promise should still be pending");
});
QUnit.module("Custom Hooks");
QUnit.test("useListener handler type", async function (assert) {
assert.expect(1);
class Parent extends Component {
constructor() {
super();
useListener('custom1', '_onCustom1');
}
}
Parent.env = makeTestEnvironment({}, () => Promise.reject());
Parent.template = xml`<div/>`;
assert.throws(() => new Parent(null), 'The handler must be a function');
});
QUnit.test("useListener in inheritance setting", async function (assert) {
assert.expect(12);
const fixture = document.body.querySelector('#qunit-fixture');
class Parent extends Component {
constructor() {
super();
useListener('custom1', this._onCustom1);
useListener('custom2', this._onCustom2);
}
_onCustom1() {
assert.step(`${this.constructor.name} custom1`);
}
_onCustom2() {
assert.step('parent custom2');
}
}
Parent.env = makeTestEnvironment({}, () => Promise.reject());
Parent.template = xml`<div/>`;
class Child extends Parent {
constructor() {
super();
useListener('custom2', this._onCustom2);
useListener('custom3', this._onCustom3);
}
_onCustom2() {
assert.step('overriden custom2');
}
_onCustom3() {
assert.step('child custom3');
}
}
const parent = new Parent(null);
const child = new Child(null);
await parent.mount(fixture);
await child.mount(fixture);
parent.trigger('custom1');
assert.verifySteps(['Parent custom1']);
parent.trigger('custom2');
assert.verifySteps(['parent custom2']);
parent.trigger('custom3');
assert.verifySteps([]);
child.trigger('custom1');
assert.verifySteps(['Child custom1']);
// There are two handlers for that one (Parent and Child)
// Although the handler is overriden in Child
child.trigger('custom2');
assert.verifySteps(['overriden custom2', 'overriden custom2']);
child.trigger('custom3');
assert.verifySteps(['child custom3']);
parent.destroy();
child.destroy();
});
QUnit.test("useListener with native JS selector", async function (assert) {
assert.expect(3);
const fixture = document.body.querySelector('#qunit-fixture');
class Parent extends Component {
constructor() {
super();
useListener('custom1', 'div .custom-class', this._onCustom1);
}
_onCustom1() {
assert.step(`custom1`);
}
}
Parent.env = makeTestEnvironment({}, () => Promise.reject());
Parent.template = xml`
<div>
<p>no trigger</p>
<h1 class="custom-class">triggers</h1>
</div>`;
const parent = new Parent(null);
await parent.mount(fixture);
parent.el.querySelector('p').dispatchEvent(new Event('custom1', {bubbles: true}));
assert.verifySteps([]);
parent.el.querySelector('h1').dispatchEvent(new Event('custom1', {bubbles: true}));
assert.verifySteps(['custom1']);
parent.destroy();
});
QUnit.test("useListener with native JS selector delegation", async function (assert) {
assert.expect(3);
const fixture = document.body.querySelector('#qunit-fixture');
class Parent extends Component {
constructor() {
super();
useListener('custom1', '.custom-class', this._onCustom1);
}
_onCustom1() {
assert.step(`custom1`);
}
}
Parent.env = makeTestEnvironment({}, () => Promise.reject());
Parent.template = xml`
<div>
<p>no trigger</p>
<h1 class="custom-class"><h2>triggers</h2></h1>
</div>`;
fixture.classList.add('custom-class');
const parent = new Parent(null);
await parent.mount(fixture);
parent.el.querySelector('p').dispatchEvent(new Event('custom1', {bubbles: true}));
assert.verifySteps([]);
parent.el.querySelector('h2').dispatchEvent(new Event('custom1', {bubbles: true}));
assert.verifySteps(['custom1']);
parent.destroy();
fixture.classList.remove('custom-class');
});
});
});