This commit converts almost all odoo module by native module. The goal is to deprecate odoo.define in favor of native module and then simplify boot.js by removing the regexp that finds module dependencies. task id: 3162300 closes odoo/odoo#117456 Signed-off-by: Géry Debongnie <ged@odoo.com>
239 lines
8.4 KiB
JavaScript
239 lines
8.4 KiB
JavaScript
/** @odoo-module **/
|
|
|
|
import makeTestEnvironment from "web.test_env";
|
|
import testUtils from "web.test_utils";
|
|
import { destroy, getFixture, mount } from "@web/../tests/helpers/utils";
|
|
import { LegacyComponent } from "@web/legacy/legacy_component";
|
|
|
|
const { xml } = owl;
|
|
import { useListener } from "@web/core/utils/hooks";
|
|
|
|
let target;
|
|
QUnit.module("web", { beforeEach() { target = getFixture(); }}, function () {
|
|
QUnit.module("Component Extension");
|
|
|
|
QUnit.test("Component destroyed while performing successful RPC", async function (assert) {
|
|
assert.expect(1);
|
|
|
|
class Parent extends LegacyComponent {}
|
|
Parent.template = xml`<div/>`;
|
|
|
|
const env = makeTestEnvironment({}, () => Promise.resolve());
|
|
|
|
const parent = await mount(Parent, target, { env });
|
|
|
|
parent.rpc({}).then(() => { throw new Error(); });
|
|
destroy(parent);
|
|
|
|
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 LegacyComponent {}
|
|
Parent.template = xml`<div/>`;
|
|
|
|
const env = makeTestEnvironment({}, () => Promise.reject());
|
|
const parent = await mount(Parent, target, { env });
|
|
|
|
parent.rpc({}).catch(() => { throw new Error(); });
|
|
destroy(parent);
|
|
|
|
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 LegacyComponent {
|
|
setup() {
|
|
useListener('custom1', '_onCustom1');
|
|
}
|
|
}
|
|
Parent.template = xml`<div/>`;
|
|
const env = makeTestEnvironment({}, () => Promise.reject());
|
|
|
|
try {
|
|
await mount(Parent, target, { env })
|
|
} catch (e) {
|
|
assert.strictEqual(e.message, 'The handler must be a function');
|
|
}
|
|
});
|
|
|
|
QUnit.test("useListener in inheritance setting", async function (assert) {
|
|
assert.expect(12);
|
|
const env = makeTestEnvironment({}, () => Promise.reject());
|
|
|
|
class Parent extends LegacyComponent {
|
|
setup() {
|
|
useListener('custom1', this._onCustom1);
|
|
useListener('custom2', this._onCustom2);
|
|
}
|
|
_onCustom1() {
|
|
assert.step(`${this.constructor.name} custom1`);
|
|
}
|
|
_onCustom2() {
|
|
assert.step('parent custom2');
|
|
}
|
|
}
|
|
Parent.template = xml`<div/>`;
|
|
|
|
class Child extends Parent {
|
|
setup() {
|
|
super.setup();
|
|
useListener('custom2', this._onCustom2);
|
|
useListener('custom3', this._onCustom3);
|
|
}
|
|
_onCustom2() {
|
|
assert.step('overriden custom2');
|
|
}
|
|
_onCustom3() {
|
|
assert.step('child custom3');
|
|
}
|
|
}
|
|
|
|
const parent = await mount(Parent, target, { env });
|
|
const child = await mount(Child, target, { env });
|
|
|
|
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']);
|
|
});
|
|
|
|
QUnit.test("useListener with native JS selector", async function (assert) {
|
|
assert.expect(3);
|
|
|
|
class Parent extends LegacyComponent {
|
|
setup() {
|
|
useListener('custom1', 'div .custom-class', this._onCustom1);
|
|
}
|
|
_onCustom1() {
|
|
assert.step(`custom1`);
|
|
}
|
|
}
|
|
Parent.template = xml`
|
|
<div>
|
|
<p>no trigger</p>
|
|
<h1 class="custom-class">triggers</h1>
|
|
</div>
|
|
`;
|
|
|
|
const env = makeTestEnvironment({}, () => Promise.reject());
|
|
const parent = await mount(Parent, target, { env });
|
|
|
|
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']);
|
|
});
|
|
|
|
QUnit.test("useListener with native JS selector delegation", async function (assert) {
|
|
assert.expect(3);
|
|
|
|
class Parent extends LegacyComponent {
|
|
setup() {
|
|
useListener('custom1', '.custom-class', this._onCustom1);
|
|
}
|
|
_onCustom1() {
|
|
assert.step(`custom1`);
|
|
}
|
|
}
|
|
Parent.template = xml`
|
|
<div>
|
|
<p>no trigger</p>
|
|
<h1 class="custom-class"><h2>triggers</h2></h1>
|
|
</div>`;
|
|
|
|
target.classList.add('custom-class');
|
|
const env = makeTestEnvironment({}, () => Promise.reject());
|
|
|
|
const parent = await mount(Parent, target, { env });
|
|
|
|
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']);
|
|
target.classList.remove('custom-class');
|
|
});
|
|
|
|
QUnit.test("useListener with capture option", async function (assert) {
|
|
assert.expect(7);
|
|
|
|
class Leaf extends LegacyComponent {
|
|
setup() {
|
|
useListener('custom1', this._onCustom1);
|
|
}
|
|
_onCustom1() {
|
|
assert.step(`${this.constructor.name} custom1`);
|
|
}
|
|
}
|
|
Leaf.template = xml`<div class="leaf"/>`;
|
|
|
|
class Root extends LegacyComponent {
|
|
setup() {
|
|
useListener('custom1', this._onCustom1, { capture: true });
|
|
}
|
|
_onCustom1(event) {
|
|
assert.step(`${this.constructor.name} custom1`);
|
|
const detail = event.detail;
|
|
if (detail && detail.stopMe) {
|
|
event.stopPropagation();
|
|
}
|
|
}
|
|
}
|
|
Root.template = xml`<div class="root"><Leaf/></div>`;
|
|
Root.components = { Leaf };
|
|
|
|
await mount(Root, target);
|
|
|
|
const rootNode = document.body.querySelector('.root');
|
|
const leafNode = document.body.querySelector('.leaf');
|
|
rootNode.dispatchEvent(new CustomEvent('custom1', {
|
|
bubbles: true,
|
|
cancelable: true
|
|
}));
|
|
assert.verifySteps(['Root custom1']);
|
|
|
|
// Dispatch custom1 on the leaf element.
|
|
// Since we listen in the capture phase, Root is first triggered.
|
|
// The event is stopped there.
|
|
leafNode.dispatchEvent(new CustomEvent('custom1', {
|
|
bubbles: true,
|
|
cancelable: true,
|
|
detail: {
|
|
stopMe: true
|
|
},
|
|
}));
|
|
assert.verifySteps(['Root custom1']);
|
|
|
|
// Same as before, except this time we don't stop the event
|
|
leafNode.dispatchEvent(new CustomEvent('custom1', {
|
|
bubbles: true,
|
|
cancelable: true,
|
|
detail: {
|
|
stopMe: false
|
|
}
|
|
}));
|
|
assert.verifySteps(['Root custom1', 'Leaf custom1']);
|
|
});
|
|
});
|