Files
odoo_source/addons/web/static/tests/components/action_menus_tests.js
T
817ce7ae5a [FIX] web: hide action button when no action
Before this commit: the action menu registry items were rendered
regardless of their inner state. The problem is that these item
components can choose not to render themselves, leading to a blank
dropdown menu if no other item was present.

To fix this, the "getProps" function of the registry has been altered to
act as validation: the item will not be rendered if the return value is
falsy. The function has also been made asynchronous to allow RPC calls.

This solution is still flawed and hides a bad design choice which is to
register components and not items or item getters; this API is too
complex for its use case(s) and adds pointless depth to the DOM. The
shape of the registry should be simplified in an improvement in master.

closes odoo/odoo#59259

Task: 2325587
X-original-commit: 45c7d881a5d130f6b3222749c8c87b6c6b45e910
Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
Co-authored-by: Julien Mougenot <jum@odoo.com>
Co-authored-by: Mohammed Shekha <msh@odoo.com>
2020-10-06 11:38:27 +00:00

252 lines
8.9 KiB
JavaScript

odoo.define('web.action_menus_tests', function (require) {
"use strict";
const ActionMenus = require('web.ActionMenus');
const Registry = require('web.Registry');
const testUtils = require('web.test_utils');
const { Component } = owl;
const cpHelpers = testUtils.controlPanel;
const { createComponent } = testUtils;
QUnit.module('Components', {
beforeEach() {
this.action = {
res_model: 'hobbit',
};
this.view = {
// needed by google_drive module, makes sense to give a view anyway.
type: 'form',
};
this.props = {
activeIds: [23],
context: {},
items: {
action: [
{ action: { id: 1 }, name: "What's taters, precious ?", id: 1 },
],
print: [
{ action: { id: 2 }, name: "Po-ta-toes", id: 2 },
],
other: [
{ description: "Boil'em", callback() { } },
{ description: "Mash'em", callback() { } },
{ description: "Stick'em in a stew", url: '#stew' },
],
},
};
// Patch the registry of the action menus
this.actionMenusRegistry = ActionMenus.registry;
ActionMenus.registry = new Registry();
},
afterEach() {
ActionMenus.registry = this.actionMenusRegistry;
},
}, function () {
QUnit.module('ActionMenus');
QUnit.test('basic interactions', async function (assert) {
assert.expect(10);
const actionMenus = await createComponent(ActionMenus, {
env: {
action: this.action,
view: this.view,
},
props: this.props,
});
const dropdowns = actionMenus.el.getElementsByClassName('o_dropdown');
assert.strictEqual(dropdowns.length, 2, "ActionMenus should contain 2 menus");
assert.strictEqual(dropdowns[0].querySelector('.o_dropdown_title').innerText.trim(), "Print");
assert.strictEqual(dropdowns[1].querySelector('.o_dropdown_title').innerText.trim(), "Action");
assert.containsNone(actionMenus, '.o_dropdown_menu');
await cpHelpers.toggleActionMenu(actionMenus, "Action");
assert.containsOnce(actionMenus, '.o_dropdown_menu');
assert.containsN(actionMenus, '.o_dropdown_menu .o_menu_item', 4);
const actionsTexts = [...dropdowns[1].querySelectorAll('.o_menu_item')].map(el => el.innerText.trim());
assert.deepEqual(actionsTexts, [
"Boil'em",
"Mash'em",
"Stick'em in a stew",
"What's taters, precious ?",
], "callbacks should appear before actions");
await cpHelpers.toggleActionMenu(actionMenus, "Print");
assert.containsOnce(actionMenus, '.o_dropdown_menu');
assert.containsN(actionMenus, '.o_dropdown_menu .o_menu_item', 1);
await cpHelpers.toggleActionMenu(actionMenus, "Print");
assert.containsNone(actionMenus, '.o_dropdown_menu');
actionMenus.destroy();
});
QUnit.test("empty action menus", async function (assert) {
assert.expect(1);
ActionMenus.registry.add("test", { Component, getProps: () => false });
this.props.items = {};
const actionMenus = await createComponent(ActionMenus, {
env: {
action: this.action,
view: this.view,
},
props: this.props,
});
assert.containsNone(actionMenus, ".o_cp_action_menus > *");
actionMenus.destroy();
});
QUnit.test('execute action', async function (assert) {
assert.expect(4);
const actionMenus = await createComponent(ActionMenus, {
env: {
action: this.action,
view: this.view,
},
props: this.props,
intercepts: {
'do-action': ev => assert.step('do-action'),
},
async mockRPC(route, args) {
switch (route) {
case '/web/action/load':
const expectedContext = {
active_id: 23,
active_ids: [23],
active_model: 'hobbit',
};
assert.deepEqual(args.context, expectedContext);
assert.step('load-action');
return { context: {}, flags: {} };
default:
return this._super(...arguments);
}
},
});
await cpHelpers.toggleActionMenu(actionMenus, "Action");
await cpHelpers.toggleMenuItem(actionMenus, "What's taters, precious ?");
assert.verifySteps(['load-action', 'do-action']);
actionMenus.destroy();
});
QUnit.test('execute callback action', async function (assert) {
assert.expect(2);
const callbackPromise = testUtils.makeTestPromise();
this.props.items.other[0].callback = function (items) {
assert.strictEqual(items.length, 1);
assert.strictEqual(items[0].description, "Boil'em");
callbackPromise.resolve();
};
const actionMenus = await createComponent(ActionMenus, {
env: {
action: this.action,
view: this.view,
},
props: this.props,
async mockRPC(route, args) {
switch (route) {
case '/web/action/load':
throw new Error("No action should be loaded.");
default:
return this._super(...arguments);
}
},
});
await cpHelpers.toggleActionMenu(actionMenus, "Action");
await cpHelpers.toggleMenuItem(actionMenus, "Boil'em");
await callbackPromise;
actionMenus.destroy();
});
QUnit.test('execute print action', async function (assert) {
assert.expect(4);
const actionMenus = await createComponent(ActionMenus, {
env: {
action: this.action,
view: this.view,
},
intercepts: {
'do-action': ev => assert.step('do-action'),
},
props: this.props,
async mockRPC(route, args) {
switch (route) {
case '/web/action/load':
const expectedContext = {
active_id: 23,
active_ids: [23],
active_model: 'hobbit',
};
assert.deepEqual(args.context, expectedContext);
assert.step('load-action');
return { context: {}, flags: {} };
default:
return this._super(...arguments);
}
},
});
await cpHelpers.toggleActionMenu(actionMenus, "Print");
await cpHelpers.toggleMenuItem(actionMenus, "Po-ta-toes");
assert.verifySteps(['load-action', 'do-action']);
actionMenus.destroy();
});
QUnit.test('execute url action', async function (assert) {
assert.expect(2);
const actionMenus = await createComponent(ActionMenus, {
env: {
action: this.action,
services: {
navigate(url) {
assert.step(url);
},
},
view: this.view,
},
props: this.props,
async mockRPC(route, args) {
switch (route) {
case '/web/action/load':
throw new Error("No action should be loaded.");
default:
return this._super(...arguments);
}
},
});
await cpHelpers.toggleActionMenu(actionMenus, "Action");
await cpHelpers.toggleMenuItem(actionMenus, "Stick'em in a stew");
assert.verifySteps(['#stew']);
actionMenus.destroy();
});
});
});