forked from Mapan/odoo17e
459 lines
17 KiB
JavaScript
459 lines
17 KiB
JavaScript
/** @odoo-module **/
|
|
|
|
import { registerCleanup } from "@web/../tests/helpers/cleanup";
|
|
import { makeTestEnv } from "@web/../tests/helpers/mock_env";
|
|
import { makeFakeLocalizationService } from "@web/../tests/helpers/mock_services";
|
|
import {
|
|
drag,
|
|
getFixture,
|
|
nextTick,
|
|
patchWithCleanup,
|
|
triggerHotkey,
|
|
} from "@web/../tests/helpers/utils";
|
|
import testUtils from "@web/../tests/legacy/helpers/test_utils";
|
|
import { templates } from "@web/core/assets";
|
|
import { browser } from "@web/core/browser/browser";
|
|
import { commandService } from "@web/core/commands/command_service";
|
|
import { hotkeyService } from "@web/core/hotkeys/hotkey_service";
|
|
import { ormService } from "@web/core/orm_service";
|
|
import { registry } from "@web/core/registry";
|
|
import { uiService } from "@web/core/ui/ui_service";
|
|
import { session } from "@web/session";
|
|
import { reorderApps } from "@web/webclient/menus/menu_helpers";
|
|
import { enterpriseSubscriptionService } from "@web_enterprise/webclient/home_menu/enterprise_subscription_service";
|
|
import { HomeMenu } from "@web_enterprise/webclient/home_menu/home_menu";
|
|
|
|
import { App, Component, EventBus, useState, xml } from "@odoo/owl";
|
|
const patchDate = testUtils.mock.patchDate;
|
|
const serviceRegistry = registry.category("services");
|
|
let target;
|
|
let appItems;
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Helpers
|
|
// -----------------------------------------------------------------------------
|
|
|
|
async function createHomeMenu(config = {}) {
|
|
const env = await makeTestEnv(config);
|
|
class HomeMenuContainer extends Component {
|
|
static props = ["*"];
|
|
static components = { HomeMenu };
|
|
static template = xml`<HomeMenu t-props="homeMenuProps"/>`;
|
|
setup() {
|
|
const apps = useState(appItems);
|
|
this.homeMenuProps = {
|
|
apps: apps,
|
|
reorderApps: (order) => {
|
|
reorderApps(apps, order);
|
|
},
|
|
};
|
|
}
|
|
}
|
|
const app = new App(HomeMenuContainer, {
|
|
env,
|
|
templates,
|
|
test: true,
|
|
});
|
|
const homeMenu = await app.mount(target);
|
|
registerCleanup(() => app.destroy());
|
|
return homeMenu;
|
|
}
|
|
|
|
async function walkOn(assert, path) {
|
|
for (const step of path) {
|
|
triggerHotkey(`${step.shiftKey ? "shift+" : ""}${step.key}`);
|
|
await nextTick();
|
|
assert.hasClass(
|
|
target.querySelectorAll(".o_menuitem")[step.index],
|
|
"o_focused",
|
|
`step ${step.number}`
|
|
);
|
|
}
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------
|
|
// Tests
|
|
// -----------------------------------------------------------------------------
|
|
|
|
let bus;
|
|
QUnit.module(
|
|
"web_enterprise",
|
|
{
|
|
beforeEach: function () {
|
|
appItems = [
|
|
{
|
|
actionID: 121,
|
|
appID: 1,
|
|
id: 1,
|
|
label: "Discuss",
|
|
parents: "",
|
|
webIcon: false,
|
|
xmlid: "app.1",
|
|
},
|
|
{
|
|
actionID: 122,
|
|
appID: 2,
|
|
id: 2,
|
|
label: "Calendar",
|
|
parents: "",
|
|
webIcon: false,
|
|
xmlid: "app.2",
|
|
},
|
|
{
|
|
actionID: 123,
|
|
appID: 3,
|
|
id: 3,
|
|
label: "Contacts",
|
|
parents: "",
|
|
webIcon: false,
|
|
xmlid: "app.3",
|
|
},
|
|
];
|
|
|
|
bus = new EventBus();
|
|
const fakeHomeMenuService = {
|
|
name: "home_menu",
|
|
start() {
|
|
return {
|
|
toggle(show) {
|
|
bus.trigger("toggle", show);
|
|
},
|
|
};
|
|
},
|
|
};
|
|
const fakeMenuService = {
|
|
name: "menu",
|
|
start() {
|
|
return {
|
|
selectMenu(menu) {
|
|
bus.trigger("selectMenu", menu.id);
|
|
},
|
|
getMenu() {
|
|
return {};
|
|
},
|
|
};
|
|
},
|
|
};
|
|
serviceRegistry.add("ui", uiService);
|
|
serviceRegistry.add("hotkey", hotkeyService);
|
|
serviceRegistry.add("command", commandService);
|
|
serviceRegistry.add("localization", makeFakeLocalizationService());
|
|
serviceRegistry.add("orm", ormService);
|
|
serviceRegistry.add(enterpriseSubscriptionService.name, enterpriseSubscriptionService);
|
|
serviceRegistry.add(fakeHomeMenuService.name, fakeHomeMenuService);
|
|
serviceRegistry.add(fakeMenuService.name, fakeMenuService);
|
|
|
|
target = getFixture();
|
|
},
|
|
},
|
|
function () {
|
|
QUnit.module("HomeMenu");
|
|
|
|
QUnit.test("ESC Support", async function (assert) {
|
|
bus.addEventListener("toggle", (ev) => {
|
|
assert.step(`toggle ${ev.detail}`);
|
|
});
|
|
await createHomeMenu();
|
|
await testUtils.dom.triggerEvent(window, "keydown", { key: "Escape" });
|
|
assert.verifySteps(["toggle false"]);
|
|
});
|
|
|
|
QUnit.test("Click on an app", async function (assert) {
|
|
bus.addEventListener("selectMenu", (ev) => {
|
|
assert.step(`selectMenu ${ev.detail}`);
|
|
});
|
|
await createHomeMenu();
|
|
|
|
await testUtils.dom.click(target.querySelectorAll(".o_menuitem")[0]);
|
|
assert.verifySteps(["selectMenu 1"]);
|
|
});
|
|
|
|
QUnit.test("Display Expiration Panel (no module installed)", async function (assert) {
|
|
const unpatchDate = patchDate(2019, 9, 10, 0, 0, 0);
|
|
registerCleanup(unpatchDate);
|
|
|
|
patchWithCleanup(session, {
|
|
expiration_date: "2019-11-01 12:00:00",
|
|
expiration_reason: "",
|
|
isMailInstalled: false,
|
|
warning: "admin",
|
|
});
|
|
|
|
await createHomeMenu();
|
|
|
|
assert.containsOnce(target, ".database_expiration_panel");
|
|
assert.strictEqual(
|
|
target.querySelector(".database_expiration_panel .oe_instance_register").innerText,
|
|
"You will be able to register your database once you have installed your first app.",
|
|
"There should be an expiration panel displayed"
|
|
);
|
|
|
|
// Close the expiration panel
|
|
await testUtils.dom.click(
|
|
target.querySelector(".database_expiration_panel .oe_instance_hide_panel")
|
|
);
|
|
assert.containsNone(target, ".database_expiration_panel");
|
|
});
|
|
|
|
QUnit.test("Navigation (only apps, only one line)", async function (assert) {
|
|
assert.expect(8);
|
|
|
|
appItems = new Array(3).fill().map((x, i) => {
|
|
return {
|
|
actionID: 120 + i,
|
|
appID: i + 1,
|
|
id: i + 1,
|
|
label: `0${i}`,
|
|
parents: "",
|
|
webIcon: false,
|
|
xmlid: `app.${i}`,
|
|
};
|
|
});
|
|
await createHomeMenu();
|
|
|
|
const path = [
|
|
{ number: 0, key: "ArrowDown", index: 0 },
|
|
{ number: 1, key: "ArrowRight", index: 1 },
|
|
{ number: 2, key: "Tab", index: 2 },
|
|
{ number: 3, key: "ArrowRight", index: 0 },
|
|
{ number: 4, key: "Tab", shiftKey: true, index: 2 },
|
|
{ number: 5, key: "ArrowLeft", index: 1 },
|
|
{ number: 6, key: "ArrowDown", index: 1 },
|
|
{ number: 7, key: "ArrowUp", index: 1 },
|
|
];
|
|
|
|
await walkOn(assert, path);
|
|
});
|
|
|
|
QUnit.test("Navigation (only apps, two lines, one incomplete)", async function (assert) {
|
|
assert.expect(19);
|
|
|
|
appItems = new Array(8).fill().map((x, i) => {
|
|
return {
|
|
actionID: 121,
|
|
appID: i + 1,
|
|
id: i + 1,
|
|
label: `0${i}`,
|
|
parents: "",
|
|
webIcon: false,
|
|
xmlid: `app.${i}`,
|
|
};
|
|
});
|
|
await createHomeMenu();
|
|
|
|
const path = [
|
|
{ number: 1, key: "ArrowRight", index: 0 },
|
|
{ number: 2, key: "ArrowUp", index: 6 },
|
|
{ number: 3, key: "ArrowUp", index: 0 },
|
|
{ number: 4, key: "ArrowDown", index: 6 },
|
|
{ number: 5, key: "ArrowDown", index: 0 },
|
|
{ number: 6, key: "ArrowRight", index: 1 },
|
|
{ number: 7, key: "ArrowRight", index: 2 },
|
|
{ number: 8, key: "ArrowUp", index: 7 },
|
|
{ number: 9, key: "ArrowUp", index: 1 },
|
|
{ number: 10, key: "ArrowRight", index: 2 },
|
|
{ number: 11, key: "ArrowDown", index: 7 },
|
|
{ number: 12, key: "ArrowDown", index: 1 },
|
|
{ number: 13, key: "ArrowUp", index: 7 },
|
|
{ number: 14, key: "ArrowRight", index: 6 },
|
|
{ number: 15, key: "ArrowLeft", index: 7 },
|
|
{ number: 16, key: "ArrowUp", index: 1 },
|
|
{ number: 17, key: "ArrowLeft", index: 0 },
|
|
{ number: 18, key: "ArrowLeft", index: 5 },
|
|
{ number: 19, key: "ArrowRight", index: 0 },
|
|
];
|
|
|
|
await walkOn(assert, path);
|
|
});
|
|
|
|
QUnit.test("Navigation and open an app in the home menu", async function (assert) {
|
|
assert.expect(7);
|
|
|
|
bus.addEventListener("selectMenu", (ev) => {
|
|
assert.step(`selectMenu ${ev.detail}`);
|
|
});
|
|
await createHomeMenu();
|
|
|
|
// No app selected so nothing to open
|
|
await testUtils.dom.triggerEvent(window, "keydown", { key: "Enter" });
|
|
assert.verifySteps([]);
|
|
|
|
const path = [
|
|
{ number: 0, key: "ArrowDown", index: 0 },
|
|
{ number: 1, key: "ArrowRight", index: 1 },
|
|
{ number: 2, key: "Tab", index: 2 },
|
|
{ number: 3, key: "shift+Tab", index: 1 },
|
|
];
|
|
|
|
await walkOn(assert, path);
|
|
|
|
// open first app (Calendar)
|
|
await testUtils.dom.triggerEvent(window, "keydown", { key: "Enter" });
|
|
|
|
assert.verifySteps(["selectMenu 2"]);
|
|
});
|
|
|
|
QUnit.test("Reorder apps in home menu using drag and drop", async function (assert) {
|
|
appItems = new Array(8).fill().map((x, i) => {
|
|
return {
|
|
actionID: 121,
|
|
appID: i + 1,
|
|
id: i + 1,
|
|
label: `0${i}`,
|
|
parents: "",
|
|
webIcon: false,
|
|
xmlid: `app.${i}`,
|
|
};
|
|
});
|
|
patchWithCleanup(browser, {
|
|
setTimeout: (callback, delay) => {
|
|
assert.step(`setTimeout of ${delay}ms`);
|
|
callback();
|
|
},
|
|
});
|
|
patchWithCleanup(session, { user_settings: { id: 1, homemenu_config: "" } });
|
|
const mockRPC = (route, args) => {
|
|
if (args.method === "set_res_users_settings") {
|
|
assert.step(`set_res_users_settings`);
|
|
return {
|
|
id: 1,
|
|
homemenu_config:
|
|
'["app.1","app.2","app.3","app.0","app.4","app.5","app.6","app.7"]',
|
|
};
|
|
}
|
|
};
|
|
const serverData = {
|
|
models: {
|
|
"res.users.settings": {
|
|
fields: {
|
|
id: {
|
|
type: "number",
|
|
},
|
|
homemenu_config: {
|
|
type: "string",
|
|
},
|
|
},
|
|
records: [
|
|
{
|
|
id: 1,
|
|
homemenu_config: "",
|
|
},
|
|
],
|
|
},
|
|
},
|
|
};
|
|
await createHomeMenu({ serverData, mockRPC });
|
|
|
|
const { drop } = await drag(".o_draggable:first-child");
|
|
await drop(".o_draggable:nth-child(4)");
|
|
assert.verifySteps(["setTimeout of 500ms", "set_res_users_settings"]);
|
|
const apps = document.querySelectorAll(".o_app");
|
|
assert.strictEqual(
|
|
apps[0].getAttribute("data-menu-xmlid"),
|
|
"app.1",
|
|
"first displayed app has app.1 xmlid"
|
|
);
|
|
assert.strictEqual(
|
|
apps[3].getAttribute("data-menu-xmlid"),
|
|
"app.0",
|
|
"app 0 is now at 4th position"
|
|
);
|
|
});
|
|
|
|
QUnit.test(
|
|
"The HomeMenu input takes the focus when you press a key only if no other element is the activeElement",
|
|
async function (assert) {
|
|
const target = getFixture();
|
|
const homeMenu = await createHomeMenu();
|
|
const input = target.querySelector(".o_search_hidden");
|
|
assert.strictEqual(document.activeElement, input);
|
|
|
|
const activeElement = document.createElement("div");
|
|
homeMenu.env.services.ui.activateElement(activeElement);
|
|
// remove the focus from the input
|
|
const otherInput = document.createElement("input");
|
|
target.querySelector(".o_home_menu").appendChild(otherInput);
|
|
otherInput.focus();
|
|
otherInput.blur();
|
|
assert.notEqual(document.activeElement, input);
|
|
|
|
await testUtils.dom.triggerEvent(window, "keydown", { key: "a" });
|
|
await nextTick();
|
|
assert.notEqual(document.activeElement, input);
|
|
|
|
homeMenu.env.services.ui.deactivateElement(activeElement);
|
|
await testUtils.dom.triggerEvent(window, "keydown", { key: "a" });
|
|
await nextTick();
|
|
assert.strictEqual(document.activeElement, input);
|
|
}
|
|
);
|
|
|
|
QUnit.test(
|
|
"The HomeMenu input does not take the focus if it is already on another input",
|
|
async function (assert) {
|
|
const target = getFixture();
|
|
await createHomeMenu();
|
|
const homeMenuInput = target.querySelector(".o_search_hidden");
|
|
assert.strictEqual(document.activeElement, homeMenuInput);
|
|
|
|
const otherInput = document.createElement("input");
|
|
target.querySelector(".o_home_menu").appendChild(otherInput);
|
|
otherInput.focus();
|
|
await testUtils.dom.triggerEvent(window, "keydown", { key: "a" });
|
|
await nextTick();
|
|
assert.notEqual(document.activeElement, homeMenuInput);
|
|
|
|
otherInput.remove();
|
|
await testUtils.dom.triggerEvent(window, "keydown", { key: "a" });
|
|
await nextTick();
|
|
assert.strictEqual(document.activeElement, homeMenuInput);
|
|
}
|
|
);
|
|
|
|
QUnit.test(
|
|
"The HomeMenu input does not take the focus if it is already on a textarea",
|
|
async function (assert) {
|
|
const target = getFixture();
|
|
await createHomeMenu();
|
|
const homeMenuInput = target.querySelector(".o_search_hidden");
|
|
assert.strictEqual(document.activeElement, homeMenuInput);
|
|
|
|
const textarea = document.createElement("textarea");
|
|
target.querySelector(".o_home_menu").appendChild(textarea);
|
|
textarea.focus();
|
|
await testUtils.dom.triggerEvent(window, "keydown", { key: "a" });
|
|
await nextTick();
|
|
assert.notEqual(document.activeElement, homeMenuInput);
|
|
|
|
textarea.remove();
|
|
await testUtils.dom.triggerEvent(window, "keydown", { key: "a" });
|
|
await nextTick();
|
|
assert.strictEqual(document.activeElement, homeMenuInput);
|
|
}
|
|
);
|
|
|
|
QUnit.test(
|
|
"home search input shouldn't be focused on touch devices [REQUIRE FOCUS]",
|
|
async function (assert) {
|
|
// patch matchMedia to alter hasTouch value
|
|
patchWithCleanup(browser, {
|
|
setTimeout: (fn) => fn(),
|
|
matchMedia: (media) => {
|
|
if (media === "(pointer:coarse)") {
|
|
return { matches: true };
|
|
}
|
|
this._super();
|
|
},
|
|
});
|
|
const target = getFixture();
|
|
await createHomeMenu();
|
|
const homeMenuInput = target.querySelector(".o_search_hidden");
|
|
assert.notOk(
|
|
homeMenuInput.matches(":focus"),
|
|
"home menu search input shouldn't have the focus"
|
|
);
|
|
}
|
|
);
|
|
}
|
|
);
|