Initial commit: Core packages

This commit is contained in:
Ernad Husremovic 2025-08-29 15:20:45 +02:00
commit 12c29a983b
9512 changed files with 8379910 additions and 0 deletions

View file

@ -0,0 +1,150 @@
/** @odoo-module **/
import { click, legacyExtraNextTick } from "@web/../tests/helpers/utils";
import {
createWebClient,
doAction,
getActionManagerServerData,
} from "@web/../tests/webclient/helpers";
import { registry } from "@web/core/registry";
import { BurgerMenu } from "@web/webclient/burger_menu/burger_menu";
import { companyService } from "@web/webclient/company_service";
/**
* Note: The asserts are all based on document.body (instead of getFixture() by example) because
* the burger menu is porteled into the dom and is not part of the qunit fixture.
*/
let serverData;
const serviceRegistry = registry.category("services");
QUnit.module("Burger Menu", {
beforeEach() {
serverData = getActionManagerServerData();
serviceRegistry.add("company", companyService);
registry.category("systray").add("burger_menu", {
Component: BurgerMenu,
});
},
});
QUnit.test("Burger menu can be opened and closed", async (assert) => {
assert.expect(2);
await createWebClient({ serverData });
await click(document.body, ".o_mobile_menu_toggle");
assert.containsOnce(document.body, ".o_burger_menu");
await click(document.body, ".o_burger_menu_close");
assert.containsNone(document.body, ".o_burger_menu");
});
QUnit.test("Burger Menu on an App", async (assert) => {
assert.expect(7);
serverData.menus[1].children = [99];
serverData.menus[99] = {
id: 99,
children: [],
name: "SubMenu",
appID: 1,
actionID: 1002,
xmlid: "",
webIconData: undefined,
webIcon: false,
};
await createWebClient({ serverData });
await click(document.body, ".o_navbar_apps_menu .dropdown-toggle");
await legacyExtraNextTick();
await click(document.body, ".o_app:nth-of-type(2)");
await legacyExtraNextTick();
assert.containsNone(document.body, ".o_burger_menu");
await click(document.body, ".o_mobile_menu_toggle");
assert.containsOnce(document.body, ".o_burger_menu");
assert.containsOnce(document.body, ".o_burger_menu nav.o_burger_menu_content li");
assert.strictEqual(
document.body.querySelector(".o_burger_menu nav.o_burger_menu_content li").textContent,
"SubMenu"
);
assert.hasClass(document.body.querySelector(".o_burger_menu_content"), "o_burger_menu_dark");
await click(document.body, ".o_burger_menu_topbar");
assert.doesNotHaveClass(
document.body.querySelector(".o_burger_menu_content"),
"o_burger_menu_dark"
);
await click(document.body, ".o_burger_menu_topbar");
assert.hasClass(document.body.querySelector(".o_burger_menu_content"), "o_burger_menu_dark");
});
QUnit.test("Burger Menu on an App without SubMenu", async (assert) => {
assert.expect(4);
await createWebClient({ serverData });
await click(document.body, ".o_navbar_apps_menu .dropdown-toggle");
await legacyExtraNextTick();
await click(document.body, ".o_app:nth-of-type(2)");
await legacyExtraNextTick();
assert.containsNone(document.body, ".o_burger_menu");
await click(document.body, ".o_mobile_menu_toggle");
assert.containsOnce(document.body, ".o_burger_menu");
assert.containsOnce(document.body, ".o_user_menu_mobile");
await click(document.body, ".o_burger_menu_close");
assert.containsNone(document.body, ".o_burger_menu");
});
QUnit.test("Burger menu closes when an action is requested", async (assert) => {
assert.expect(3);
const wc = await createWebClient({ serverData });
await click(document.body, ".o_mobile_menu_toggle");
assert.containsOnce(document.body, ".o_burger_menu");
await doAction(wc, 1);
await legacyExtraNextTick();
assert.containsNone(document.body, ".o_burger_menu");
assert.containsOnce(document.body, ".o_kanban_view");
});
QUnit.test("Burger menu closes when click on menu item", async (assert) => {
serverData.actions[1].target = "new";
serverData.menus[1].children = [99];
serverData.menus[99] = {
id: 99,
children: [],
name: "SubMenu",
appID: 1,
actionID: 1,
xmlid: "",
webIconData: undefined,
webIcon: false,
};
await createWebClient({ serverData });
await click(document.body, ".o_navbar_apps_menu .dropdown-toggle");
await legacyExtraNextTick();
await click(document.body, ".o_app:nth-of-type(2)");
await legacyExtraNextTick();
assert.containsNone(document.body, ".o_burger_menu");
await click(document.body, ".o_mobile_menu_toggle");
assert.containsOnce(document.body, ".o_burger_menu");
assert.strictEqual(
document.body.querySelector(".o_burger_menu nav.o_burger_menu_content li").textContent,
"SubMenu"
);
await click(document.body, ".o_burger_menu nav.o_burger_menu_content li");
await legacyExtraNextTick();
await legacyExtraNextTick();
assert.containsNone(document.body, ".o_burger_menu");
});

View file

@ -0,0 +1,146 @@
/** @odoo-module **/
import { ormService } from "@web/core/orm_service";
import { registry } from "@web/core/registry";
import { hotkeyService } from "@web/core/hotkeys/hotkey_service";
import { BurgerUserMenu } from "@web/webclient/burger_menu/burger_user_menu/burger_user_menu";
import { preferencesItem } from "@web/webclient/user_menu/user_menu_items";
import { userService } from "@web/core/user_service";
import { makeTestEnv } from "@web/../tests/helpers/mock_env";
import { makeFakeLocalizationService } from "@web/../tests/helpers/mock_services";
import { click, getFixture, mount } from "@web/../tests/helpers/utils";
const serviceRegistry = registry.category("services");
const userMenuRegistry = registry.category("user_menuitems");
let target;
let env;
QUnit.module("BurgerUserMenu", {
async beforeEach() {
serviceRegistry.add("user", userService);
serviceRegistry.add("hotkey", hotkeyService);
target = getFixture();
},
});
QUnit.test("can be rendered", async (assert) => {
env = await makeTestEnv();
userMenuRegistry.add("bad_item", function () {
return {
type: "item",
id: "bad",
description: "Bad",
callback: () => {
assert.step("callback bad_item");
},
sequence: 10,
};
});
userMenuRegistry.add("ring_item", function () {
return {
type: "item",
id: "ring",
description: "Ring",
callback: () => {
assert.step("callback ring_item");
},
sequence: 5,
};
});
userMenuRegistry.add("frodo_item", function () {
return {
type: "switch",
id: "frodo",
description: "Frodo",
callback: () => {
assert.step("callback frodo_item");
},
sequence: 11,
};
});
userMenuRegistry.add("separator", function () {
return {
type: "separator",
sequence: 15,
};
});
userMenuRegistry.add("invisible_item", function () {
return {
type: "item",
id: "hidden",
description: "Hidden Power",
callback: () => {},
sequence: 5,
hide: true,
};
});
userMenuRegistry.add("eye_item", function () {
return {
type: "item",
id: "eye",
description: "Eye",
callback: () => {
assert.step("callback eye_item");
},
};
});
await mount(BurgerUserMenu, target, { env });
assert.containsN(target, ".o_user_menu_mobile .dropdown-item", 4);
assert.containsOnce(target, ".o_user_menu_mobile .dropdown-item input.form-check-input");
assert.containsOnce(target, "div.dropdown-divider");
const children = [...(target.querySelector(".o_user_menu_mobile").children || [])];
assert.deepEqual(
children.map((el) => el.tagName),
["A", "A", "DIV", "DIV", "A"]
);
const items = [...target.querySelectorAll(".dropdown-item")] || [];
assert.deepEqual(
items.map((el) => el.textContent),
["Ring", "Bad", "Frodo", "Eye"]
);
for (const item of items) {
click(item);
}
assert.verifySteps([
"callback ring_item",
"callback bad_item",
"callback frodo_item",
"callback eye_item",
]);
});
QUnit.test("can execute the callback of settings", async (assert) => {
const mockRPC = (route) => {
if (route === "/web/dataset/call_kw/res.users/action_get") {
return Promise.resolve({
name: "Change My Preferences",
res_id: 0,
});
}
};
const testConfig = { mockRPC };
serviceRegistry.add("localization", makeFakeLocalizationService());
serviceRegistry.add("orm", ormService);
const fakeActionService = {
name: "action",
start() {
return {
doAction(actionId) {
assert.step("" + actionId.res_id);
assert.step(actionId.name);
return Promise.resolve(true);
},
};
},
};
serviceRegistry.add("action", fakeActionService, { force: true });
env = await makeTestEnv(testConfig);
userMenuRegistry.add("profile", preferencesItem);
await mount(BurgerUserMenu, target, { env });
assert.containsOnce(target, ".o_user_menu_mobile .dropdown-item");
const item = target.querySelector(".o_user_menu_mobile .dropdown-item");
assert.strictEqual(item.textContent, "Preferences");
await click(item);
assert.verifySteps(["7", "Change My Preferences"]);
});

View file

@ -0,0 +1,172 @@
/** @odoo-module **/
import { getFixture, mockTimeout, nextTick } from "@web/../tests/helpers/utils";
import { makeView, setupViewRegistries } from "@web/../tests/views/helpers";
import { swipeLeft, swipeRight } from "@web/../tests/mobile/helpers";
import { registry } from "@web/core/registry";
import { EventBus } from "@odoo/owl";
let serverData, target;
const serviceRegistry = registry.category("services");
QUnit.module("Mobile SettingsFormView", (hooks) => {
hooks.beforeEach(() => {
serverData = {
models: {
project: {
fields: {
foo: { string: "Foo", type: "boolean" },
bar: { string: "Bar", type: "boolean" },
},
},
},
};
target = getFixture();
setupViewRegistries();
});
QUnit.module("BaseSettings Mobile");
QUnit.test("swipe settings in mobile [REQUIRE TOUCHEVENT]", async function (assert) {
const { execRegisteredTimeouts } = mockTimeout();
serviceRegistry.add("ui", {
start(env) {
Object.defineProperty(env, "isSmall", {
value: true,
});
return {
bus: new EventBus(),
size: 0,
isSmall: true,
};
},
});
await makeView({
type: "form",
resModel: "project",
serverData,
arch: `
<form string="Settings" class="oe_form_configuration o_base_settings" js_class="base_settings">
<div class="o_setting_container">
<div class="settings">
<div class="app_settings_block" string="CRM" data-key="crm">
<div class="row mt16 o_settings_container">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="bar"/>
</div>
<div class="o_setting_right_pane">
<label for="bar"/>
<div class="text-muted">this is bar</div>
</div>
</div>
</div>
</div>
<div class="app_settings_block" string="Project" data-key="project">
<div class="row mt16 o_settings_container">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="foo"/>
</div>
<div class="o_setting_right_pane">
<label for="foo"/>
<div class="text-muted">this is foo</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>`,
});
await swipeLeft(target, ".settings");
execRegisteredTimeouts();
await nextTick();
assert.hasAttrValue(
target.querySelector(".selected"),
"data-key",
"project",
"current setting should be project"
);
await swipeRight(target, ".settings");
execRegisteredTimeouts();
await nextTick();
assert.hasAttrValue(
target.querySelector(".selected"),
"data-key",
"crm",
"current setting should be crm"
);
});
QUnit.test(
"swipe settings on larger screen sizes has no effect [REQUIRE TOUCHEVENT]",
async function (assert) {
const { execRegisteredTimeouts } = mockTimeout();
serviceRegistry.add("ui", {
start(env) {
Object.defineProperty(env, "isSmall", {
value: false,
});
return {
bus: new EventBus(),
size: 9,
isSmall: false,
};
},
});
await makeView({
type: "form",
resModel: "project",
serverData,
arch: `
<form string="Settings" class="oe_form_configuration o_base_settings" js_class="base_settings">
<div class="o_setting_container">
<div class="settings">
<div class="app_settings_block" string="CRM" data-key="crm">
<div class="row mt16 o_settings_container">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="bar"/>
</div>
<div class="o_setting_right_pane">
<label for="bar"/>
<div class="text-muted">this is bar</div>
</div>
</div>
</div>
</div>
<div class="app_settings_block" string="Project" data-key="project">
<div class="row mt16 o_settings_container">
<div class="col-12 col-lg-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="foo"/>
</div>
<div class="o_setting_right_pane">
<label for="foo"/>
<div class="text-muted">this is foo</div>
</div>
</div>
</div>
</div>
</div>
</div>
</form>`,
});
await swipeLeft(target, ".settings");
execRegisteredTimeouts();
await nextTick();
assert.hasAttrValue(
target.querySelector(".selected"),
"data-key",
"crm",
"current setting should still be crm"
);
}
);
});