802 lines
28 KiB
JavaScript
802 lines
28 KiB
JavaScript
import { beforeEach, describe, expect, test } from "@odoo/hoot";
|
|
import { click, keyDown, queryAll, queryFirst } from "@odoo/hoot-dom";
|
|
import { animationFrame, Deferred, mockMatchMedia } from "@odoo/hoot-mock";
|
|
import { Component, onMounted, xml } from "@odoo/owl";
|
|
import {
|
|
clearRegistry,
|
|
contains,
|
|
defineActions,
|
|
defineMenus,
|
|
defineModels,
|
|
fields,
|
|
getMockEnv,
|
|
getService,
|
|
models,
|
|
mountWithCleanup,
|
|
mountWebClient,
|
|
onRpc,
|
|
patchWithCleanup,
|
|
serverState,
|
|
stepAllNetworkCalls,
|
|
} from "@web/../tests/web_test_helpers";
|
|
import { browser } from "@web/core/browser/browser";
|
|
import { router } from "@web/core/browser/router";
|
|
import { registry } from "@web/core/registry";
|
|
import { config as transitionConfig } from "@web/core/transition";
|
|
import { user } from "@web/core/user";
|
|
import { redirect } from "@web/core/utils/urls";
|
|
import { UserMenu } from "@web/webclient/user_menu/user_menu";
|
|
import { shareUrlMenuItem } from "@web_enterprise/webclient/share_url/share_url";
|
|
import { WebClientEnterprise } from "@web_enterprise/webclient/webclient";
|
|
|
|
const actionRegistry = registry.category("actions");
|
|
|
|
async function goToHomeMenu() {
|
|
await click(".o_menu_toggle");
|
|
await animationFrame();
|
|
|
|
if (getMockEnv().isSmall) {
|
|
await click(queryFirst(".o_sidebar_topbar a.btn-primary", { root: document.body }));
|
|
await animationFrame();
|
|
}
|
|
}
|
|
|
|
defineActions([
|
|
{
|
|
id: 1,
|
|
xml_id: "action_1",
|
|
name: "Partners Action 1",
|
|
res_model: "partner",
|
|
views: [[false, "kanban"]],
|
|
},
|
|
{
|
|
id: 2,
|
|
xml_id: "action_2",
|
|
type: "ir.actions.server",
|
|
},
|
|
{
|
|
id: 3,
|
|
xml_id: "action_3",
|
|
name: "Partners",
|
|
res_model: "partner",
|
|
views: [
|
|
[false, "list"],
|
|
[false, "kanban"],
|
|
[false, "form"],
|
|
],
|
|
},
|
|
{
|
|
id: 4,
|
|
xml_id: "action_4",
|
|
name: "Partners Action 4",
|
|
res_model: "partner",
|
|
views: [
|
|
[false, "kanban"],
|
|
[false, "list"],
|
|
[false, "form"],
|
|
],
|
|
},
|
|
{
|
|
id: 5,
|
|
xml_id: "action_5",
|
|
name: "Create a Partner",
|
|
res_model: "partner",
|
|
target: "new",
|
|
views: [[false, "form"]],
|
|
},
|
|
{
|
|
id: 6,
|
|
xml_id: "action_6",
|
|
name: "Partner",
|
|
res_id: 2,
|
|
res_model: "partner",
|
|
views: [[false, "form"]],
|
|
},
|
|
{
|
|
id: 1001,
|
|
tag: "__test__client__action__",
|
|
target: "main",
|
|
type: "ir.actions.client",
|
|
params: { description: "Id 1" },
|
|
},
|
|
{
|
|
id: 1002,
|
|
tag: "__test__client__action__",
|
|
target: "main",
|
|
type: "ir.actions.client",
|
|
params: { description: "Id 2" },
|
|
},
|
|
]);
|
|
|
|
defineMenus([
|
|
{ id: 0 }, // prevents auto-loading the first action
|
|
{ id: 1, name: "App1", appID: 1, actionID: 1001, xmlid: "menu_1" },
|
|
{ id: 2, name: "App2", appID: 2, actionID: 1002, xmlid: "menu_2" },
|
|
]);
|
|
class Partner extends models.Model {
|
|
name = fields.Char();
|
|
foo = fields.Char();
|
|
parent_id = fields.Many2one({ relation: "partner" });
|
|
child_ids = fields.One2many({ relation: "partner", relation_field: "parent_id" });
|
|
|
|
_records = [
|
|
{ id: 1, name: "First record", foo: "yop", parent_id: 3 },
|
|
{ id: 2, name: "Second record", foo: "blip", parent_id: 3 },
|
|
{ id: 3, name: "Third record", foo: "gnap", parent_id: 1 },
|
|
{ id: 4, name: "Fourth record", foo: "plop", parent_id: 1 },
|
|
{ id: 5, name: "Fifth record", foo: "zoup", parent_id: 1 },
|
|
];
|
|
_views = {
|
|
kanban: `
|
|
<kanban>
|
|
<templates>
|
|
<t t-name="card">
|
|
<field name="foo"/>
|
|
</t>
|
|
</templates>
|
|
</kanban>
|
|
`,
|
|
list: `<list><field name="foo"/></list>`,
|
|
form: `
|
|
<form>
|
|
<header>
|
|
<button name="object" string="Call method" type="object"/>
|
|
<button name="4" string="Execute action" type="action"/>
|
|
</header>
|
|
<group>
|
|
<field name="name"/>
|
|
<field name="foo"/>
|
|
</group>
|
|
</form>
|
|
`,
|
|
search: `<search><field name="foo" string="Foo"/></search>`,
|
|
};
|
|
}
|
|
defineModels([Partner]);
|
|
class TestClientAction extends Component {
|
|
static template = xml`
|
|
<div class="test_client_action">
|
|
ClientAction_<t t-esc="props.action.params?.description"/>
|
|
</div>
|
|
`;
|
|
static props = ["*"];
|
|
|
|
setup() {
|
|
onMounted(() => {
|
|
this.env.config.setDisplayName(`Client action ${this.props.action.id}`);
|
|
});
|
|
}
|
|
}
|
|
|
|
onRpc("has_group", () => true);
|
|
|
|
beforeEach(() => {
|
|
actionRegistry.add("__test__client__action__", TestClientAction);
|
|
patchWithCleanup(transitionConfig, { disabled: true });
|
|
});
|
|
// Should test ONLY the webClient and features present in Enterprise
|
|
// Those tests rely on hidden view to be in CSS: display: none
|
|
describe("basic flow with home menu", () => {
|
|
stepAllNetworkCalls();
|
|
onRpc("partner", "get_formview_action", () => ({
|
|
type: "ir.actions.act_window",
|
|
res_model: "partner",
|
|
view_type: "form",
|
|
view_mode: "form",
|
|
views: [[false, "form"]],
|
|
target: "current",
|
|
res_id: 2,
|
|
}));
|
|
defineMenus(
|
|
[
|
|
{
|
|
id: 1,
|
|
name: "App1",
|
|
appID: 1,
|
|
actionID: 4,
|
|
xmlid: "menu_1",
|
|
},
|
|
],
|
|
{ mode: "replace" }
|
|
);
|
|
test("1 -- start up", async () => {
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
expect.verifySteps(["/web/webclient/translations", "/web/webclient/load_menus"]);
|
|
expect(document.body).toHaveClass("o_home_menu_background");
|
|
expect(".o_home_menu").toHaveCount(1);
|
|
expect(".o_menu_toggle").not.toBeVisible();
|
|
expect(".o_app.o_menuitem").toHaveCount(1);
|
|
});
|
|
|
|
test("2 -- navbar updates on displaying an action", async () => {
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
expect.verifySteps(["/web/webclient/translations", "/web/webclient/load_menus"]);
|
|
await contains(".o_app.o_menuitem").click();
|
|
await animationFrame();
|
|
expect.verifySteps(["/web/action/load", "get_views", "web_search_read", "has_group"]);
|
|
expect(document.body).not.toHaveClass("o_home_menu_background");
|
|
expect(".o_home_menu").toHaveCount(0);
|
|
expect(".o_kanban_view").toHaveCount(1);
|
|
expect(".o_menu_toggle").toBeVisible();
|
|
expect(".o_menu_toggle").not.toHaveClass("o_menu_toggle_back");
|
|
});
|
|
|
|
test("3 -- push another action in the breadcrumb", async () => {
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
expect.verifySteps(["/web/webclient/translations", "/web/webclient/load_menus"]);
|
|
await contains(".o_app.o_menuitem").click();
|
|
await animationFrame();
|
|
expect.verifySteps(["/web/action/load", "get_views", "web_search_read", "has_group"]);
|
|
expect(".o_kanban_view").toHaveCount(1);
|
|
await contains(".o_kanban_record").click();
|
|
await animationFrame(); // there is another tick to update navbar and destroy HomeMenu
|
|
expect.verifySteps(["web_read"]);
|
|
expect(".o_menu_toggle").toBeVisible();
|
|
expect(".o_form_view").toHaveCount(1);
|
|
expect(".o_breadcrumb .active").toHaveText("First record");
|
|
});
|
|
|
|
test.tags("desktop");
|
|
test("4 -- push a third action in the breadcrumb", async () => {
|
|
Partner._views["form"] = `
|
|
<form>
|
|
<field name="display_name"/>
|
|
<field name="parent_id" open_target="current"/>
|
|
</form>
|
|
`;
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
expect.verifySteps(["/web/webclient/translations", "/web/webclient/load_menus"]);
|
|
await contains(".o_app.o_menuitem").click();
|
|
await animationFrame();
|
|
expect.verifySteps(["/web/action/load", "get_views", "web_search_read", "has_group"]);
|
|
expect(".o_kanban_view").toHaveCount(1);
|
|
await contains(".o_kanban_record").click();
|
|
expect.verifySteps(["web_read"]);
|
|
await contains('.o_field_widget[name="parent_id"] .o_external_button', {
|
|
visible: false,
|
|
}).click();
|
|
expect.verifySteps(["get_formview_action", "get_views", "web_read"]);
|
|
expect(".o_form_view").toHaveCount(1);
|
|
expect(".o_breadcrumb .active").toHaveText("Second record");
|
|
// The third one is the active one
|
|
expect(".breadcrumb-item").toHaveCount(2);
|
|
});
|
|
|
|
test("5 -- switch to HomeMenu from an action with 2 breadcrumbs", async () => {
|
|
Partner._views["form"] = `
|
|
<form>
|
|
<field name="display_name"/>
|
|
<field name="parent_id" open_target="current"/>
|
|
</form>
|
|
`;
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
expect.verifySteps(["/web/webclient/translations", "/web/webclient/load_menus"]);
|
|
await contains(".o_app.o_menuitem").click();
|
|
await animationFrame();
|
|
expect.verifySteps(["/web/action/load", "get_views", "web_search_read", "has_group"]);
|
|
expect(".o_kanban_view").toHaveCount(1);
|
|
await contains(".o_kanban_record").click();
|
|
expect.verifySteps(["web_read"]);
|
|
await contains('.o_field_widget[name="parent_id"] .o_external_button', {
|
|
visible: false,
|
|
}).click();
|
|
expect.verifySteps(["get_formview_action", "get_views", "web_read"]);
|
|
await goToHomeMenu();
|
|
expect.verifySteps([]);
|
|
expect(".o_menu_toggle").toHaveClass("o_menu_toggle_back");
|
|
expect(".o_home_menu").toHaveCount(1);
|
|
expect(".o_form_view").not.toHaveCount();
|
|
});
|
|
|
|
test.tags("desktop");
|
|
test("6 -- back to underlying action with many breadcrumbs", async () => {
|
|
Partner._views["form"] = `
|
|
<form>
|
|
<field name="display_name"/>
|
|
<field name="parent_id" open_target="current"/>
|
|
</form>
|
|
`;
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
expect.verifySteps(["/web/webclient/translations", "/web/webclient/load_menus"]);
|
|
await contains(".o_app.o_menuitem").click();
|
|
await animationFrame();
|
|
expect.verifySteps(["/web/action/load", "get_views", "web_search_read", "has_group"]);
|
|
expect(".o_kanban_view").toHaveCount(1);
|
|
await contains(".o_kanban_record").click();
|
|
expect.verifySteps(["web_read"]);
|
|
await contains('.o_field_widget[name="parent_id"] .o_external_button', {
|
|
visible: false,
|
|
}).click();
|
|
expect.verifySteps(["get_formview_action", "get_views", "web_read"]);
|
|
await contains(".o_menu_toggle").click();
|
|
|
|
// can't click again too soon because of the mutex in home_menu
|
|
// service (waiting for the url to be updated)
|
|
await animationFrame();
|
|
|
|
await contains(".o_menu_toggle").click();
|
|
|
|
expect.verifySteps(["web_read"]);
|
|
expect(".o_home_menu").toHaveCount(0);
|
|
expect(".o_form_view").toHaveCount(1);
|
|
expect(".o_menu_toggle").not.toHaveClass("o_menu_toggle_back");
|
|
expect(".o_breadcrumb .active").toHaveText("Second record");
|
|
// Third breadcrumb is the active one
|
|
expect(".breadcrumb-item").toHaveCount(2);
|
|
});
|
|
});
|
|
|
|
test("restore the newly created record in form view", async () => {
|
|
defineActions(
|
|
[
|
|
{
|
|
id: 6,
|
|
xml_id: "action_6",
|
|
name: "Partner",
|
|
res_model: "partner",
|
|
views: [[false, "form"]],
|
|
},
|
|
],
|
|
{ mode: "replace" }
|
|
);
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
|
|
await getService("action").doAction(6);
|
|
expect(".o_form_view").toHaveCount(1);
|
|
expect(".o_form_view .o_form_editable").toHaveCount(1);
|
|
await contains(".o_field_widget[name=name] input").edit("red right hand");
|
|
await contains(".o_form_button_save").click();
|
|
expect(".o_breadcrumb .active").toHaveText("red right hand");
|
|
await goToHomeMenu();
|
|
expect(".o_form_view").not.toHaveCount();
|
|
|
|
// can't click again too soon because of the mutex in home_menu
|
|
// service (waiting for the url to be updated)
|
|
await animationFrame();
|
|
|
|
await contains(".o_menu_toggle").click();
|
|
expect(".o_form_view").toHaveCount(1);
|
|
expect(".o_form_view .o_form_saved").toHaveCount(1);
|
|
expect(".o_breadcrumb .active").toHaveText("red right hand");
|
|
});
|
|
|
|
test.tags("desktop");
|
|
test("fast clicking on restore (implementation detail)", async () => {
|
|
expect.assertions(8);
|
|
|
|
let doVeryFastClick = false;
|
|
|
|
class DelayedClientAction extends Component {
|
|
static template = xml`<div class='delayed_client_action'>
|
|
<button t-on-click="resolve">RESOLVE</button>
|
|
</div>`;
|
|
static props = ["*"];
|
|
setup() {
|
|
onMounted(() => {
|
|
if (doVeryFastClick) {
|
|
doVeryFastClick = false;
|
|
click(".o_menu_toggle"); // go to home menu
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
registry.category("actions").add("DelayedClientAction", DelayedClientAction);
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
await getService("action").doAction("DelayedClientAction");
|
|
await animationFrame();
|
|
await contains(".o_menu_toggle").click(); // go to home menu
|
|
expect(".o_home_menu").toBeVisible();
|
|
expect(".delayed_client_action").not.toHaveCount();
|
|
|
|
doVeryFastClick = true;
|
|
await contains(".o_menu_toggle").click(); // back
|
|
expect(".o_home_menu").toHaveCount(0);
|
|
expect(".delayed_client_action").toHaveCount(1);
|
|
await animationFrame(); // waiting for DelayedClientAction
|
|
expect(".o_home_menu").toBeVisible();
|
|
expect(".delayed_client_action").not.toHaveCount();
|
|
|
|
await contains(".o_menu_toggle").click(); // back
|
|
await animationFrame();
|
|
expect(".o_home_menu").toHaveCount(0);
|
|
expect(".delayed_client_action").toHaveCount(1);
|
|
});
|
|
|
|
test("clear unCommittedChanges when toggling home menu", async () => {
|
|
expect.assertions(6);
|
|
// Edit a form view, don't save, toggle home menu
|
|
// the autosave feature of the Form view is activated
|
|
// and relied upon by this test
|
|
|
|
onRpc("web_save", ({ args, model }) => {
|
|
expect(model).toBe("partner");
|
|
expect(args[1]).toEqual({
|
|
name: "red right hand",
|
|
foo: false,
|
|
});
|
|
});
|
|
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
await getService("action").doAction(3, { viewType: "form" });
|
|
expect(".o_form_view .o_form_editable").toHaveCount(1);
|
|
await contains(".o_field_widget[name=name] input").edit("red right hand");
|
|
|
|
await goToHomeMenu();
|
|
expect(".o_form_view").toHaveCount(0);
|
|
expect(".modal").toHaveCount(0);
|
|
expect(".o_home_menu").toHaveCount(1);
|
|
});
|
|
|
|
test("can have HomeMenu and dialog action", async () => {
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
expect(".o_home_menu").toHaveCount(1);
|
|
expect(".modal .o_form_view").toHaveCount(0);
|
|
await getService("action").doAction(5);
|
|
expect(".modal .o_form_view").toHaveCount(1);
|
|
expect(".modal .o_form_view").toBeVisible();
|
|
expect(".o_home_menu").toHaveCount(1);
|
|
});
|
|
|
|
test("supports attachments of apps deleted", async () => {
|
|
// When doing a pg_restore without the filestore
|
|
// LPE fixme: may not be necessary anymore since menus are not HomeMenu props anymore
|
|
defineMenus([
|
|
{
|
|
id: 1,
|
|
appID: 1,
|
|
actionID: 1,
|
|
xmlid: "",
|
|
name: "Partners",
|
|
webIconData: "",
|
|
webIcon: "bloop,bloop",
|
|
},
|
|
]);
|
|
serverState.debug = "1";
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
expect(".o_home_menu").toHaveCount(1);
|
|
});
|
|
|
|
test.tags("desktop");
|
|
test("debug manager resets to global items when home menu is displayed", async () => {
|
|
const debugRegistry = registry.category("debug");
|
|
debugRegistry.category("default").add("item_1", () => ({
|
|
type: "item",
|
|
description: "globalItem",
|
|
callback: () => {},
|
|
sequence: 10,
|
|
}));
|
|
onRpc("has_access", () => true);
|
|
serverState.debug = "1";
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
await contains(".o_debug_manager .dropdown-toggle").click();
|
|
expect(".dropdown-item:contains('globalItem')").toHaveCount(1);
|
|
expect(".dropdown-item:contains('View: Kanban')").toHaveCount(0);
|
|
|
|
await contains(".o_debug_manager .dropdown-toggle").click();
|
|
await getService("action").doAction(1);
|
|
await contains(".o_debug_manager .dropdown-toggle").click();
|
|
expect(".dropdown-item:contains('globalItem')").toHaveCount(1);
|
|
expect(".dropdown-item:contains('View: Kanban')").toHaveCount(1);
|
|
|
|
await contains(".o_menu_toggle").click();
|
|
await contains(".o_debug_manager .dropdown-toggle").click();
|
|
expect(".dropdown-item:contains('globalItem')").toHaveCount(1);
|
|
expect(".dropdown-item:contains('View: Kanban')").toHaveCount(0);
|
|
|
|
await contains(".o_debug_manager .dropdown-toggle").click();
|
|
await getService("action").doAction(3);
|
|
await contains(".o_debug_manager .dropdown-toggle").click();
|
|
expect(".dropdown-item:contains('globalItem')").toHaveCount(1);
|
|
expect(".dropdown-item:contains('View: List')").toHaveCount(1);
|
|
expect(".dropdown-item:contains('View: Kanban')").toHaveCount(0);
|
|
});
|
|
|
|
test("url state is well handled when going in and out of the HomeMenu", async () => {
|
|
patchWithCleanup(browser.location, {
|
|
origin: "http://example.com",
|
|
});
|
|
redirect("/odoo");
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
expect(router.current).toEqual({
|
|
action: "menu",
|
|
actionStack: [
|
|
{
|
|
action: "menu",
|
|
displayName: "Home",
|
|
},
|
|
],
|
|
});
|
|
expect(browser.history.length).toBe(1);
|
|
|
|
await contains(".o_apps > .o_draggable:eq(1) > .o_app").click();
|
|
await animationFrame();
|
|
expect(router.current).toEqual({
|
|
action: 1002,
|
|
actionStack: [
|
|
{
|
|
action: 1002,
|
|
displayName: "Client action 1002",
|
|
},
|
|
],
|
|
});
|
|
expect(browser.history.length).toBe(2);
|
|
expect(browser.location.href).toBe("http://example.com/odoo/action-1002");
|
|
|
|
await goToHomeMenu();
|
|
await animationFrame();
|
|
expect(router.current).toEqual(
|
|
{
|
|
action: "menu",
|
|
actionStack: [
|
|
{
|
|
action: 1002,
|
|
displayName: "Client action 1002",
|
|
},
|
|
{
|
|
action: "menu",
|
|
displayName: "Home",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
message:
|
|
"the actionStack is required to be able to restore the menu toggle back button and the underlying breadcrumbs",
|
|
}
|
|
);
|
|
expect(browser.history.length).toBe(3);
|
|
expect(browser.location.href).toBe("http://example.com/odoo", {
|
|
message:
|
|
"despite the actionStack being in the router state, the url shouldn't have any path",
|
|
});
|
|
|
|
await contains(".o_apps > .o_draggable:eq(0) > .o_app").click();
|
|
await animationFrame();
|
|
expect(router.current).toEqual(
|
|
{
|
|
action: 1001,
|
|
actionStack: [
|
|
{
|
|
action: 1001,
|
|
displayName: "Client action 1001",
|
|
},
|
|
],
|
|
},
|
|
{ message: "clicking another app creates a new action stack (ie empties the breadcrumb)" }
|
|
);
|
|
expect(browser.history.length).toBe(4);
|
|
expect(browser.location.href).toBe("http://example.com/odoo/action-1001");
|
|
|
|
browser.history.back();
|
|
await animationFrame();
|
|
expect(router.current).toEqual(
|
|
{
|
|
action: "menu",
|
|
actionStack: [
|
|
{
|
|
action: 1002,
|
|
displayName: "Client action 1002",
|
|
},
|
|
{
|
|
action: "menu",
|
|
displayName: "Home",
|
|
},
|
|
],
|
|
globalState: {},
|
|
},
|
|
{ message: "actionStack was restored" }
|
|
);
|
|
expect(browser.history.length).toBe(4, {
|
|
message: "the previous history entry still exists (available with forward button)",
|
|
});
|
|
expect(browser.location.href).toBe("http://example.com/odoo");
|
|
|
|
await contains(".o_menu_toggle").click();
|
|
await animationFrame();
|
|
expect(router.current).toEqual({
|
|
action: 1002,
|
|
actionStack: [
|
|
{
|
|
action: 1002,
|
|
displayName: "Client action 1002",
|
|
},
|
|
],
|
|
});
|
|
expect(browser.history.length).toBe(4);
|
|
expect(browser.location.href).toBe("http://example.com/odoo/action-1002");
|
|
});
|
|
|
|
test.tags("desktop");
|
|
test("underlying action's menu items are invisible when HomeMenu is displayed", async () => {
|
|
defineMenus([
|
|
{
|
|
id: 1,
|
|
children: [
|
|
{
|
|
id: 99,
|
|
name: "SubMenu",
|
|
appID: 1,
|
|
actionID: 1002,
|
|
xmlid: "",
|
|
webIconData: undefined,
|
|
webIcon: false,
|
|
},
|
|
],
|
|
},
|
|
]);
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
expect("nav .o_menu_sections").toHaveCount(0);
|
|
expect("nav .o_menu_brand").toHaveCount(0);
|
|
await contains(".o_app.o_menuitem:nth-child(1)").click();
|
|
await animationFrame();
|
|
expect("nav .o_menu_sections").toHaveCount(1);
|
|
expect("nav .o_menu_brand").toHaveCount(1);
|
|
expect(".o_menu_sections").toBeVisible();
|
|
expect(".o_menu_brand").toBeVisible();
|
|
await contains(".o_menu_toggle").click();
|
|
expect("nav .o_menu_sections").toHaveCount(1);
|
|
expect("nav .o_menu_brand").toHaveCount(1);
|
|
expect(".o_menu_sections").not.toBeVisible();
|
|
expect(".o_menu_brand").not.toBeVisible();
|
|
});
|
|
|
|
test("go back to home menu using browser back button", async () => {
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
expect(".o_home_menu").toHaveCount(1);
|
|
expect(".o_main_navbar .o_menu_toggle").not.toBeVisible();
|
|
|
|
await contains(".o_apps > .o_draggable:nth-child(2) > .o_app").click();
|
|
expect(".test_client_action").toHaveCount(0);
|
|
await animationFrame();
|
|
expect(".test_client_action").toHaveCount(1);
|
|
expect(".o_home_menu").toHaveCount(0);
|
|
|
|
browser.history.back();
|
|
await animationFrame();
|
|
await animationFrame();
|
|
expect(".test_client_action").toHaveCount(0);
|
|
expect(".o_home_menu").toHaveCount(1);
|
|
expect(".o_main_navbar .o_menu_toggle").not.toBeVisible();
|
|
});
|
|
|
|
test("initial action crashes", async () => {
|
|
expect.errors(1);
|
|
redirect("/odoo/action-__test__client__action__?menu_id=1");
|
|
const ClientAction = registry.category("actions").get("__test__client__action__");
|
|
class Override extends ClientAction {
|
|
setup() {
|
|
super.setup();
|
|
expect.step("clientAction setup");
|
|
throw new Error("my error");
|
|
}
|
|
}
|
|
registry.category("actions").add("__test__client__action__", Override, { force: true });
|
|
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
expect.verifySteps(["clientAction setup"]);
|
|
expect("nav .o_menu_toggle").toHaveCount(1);
|
|
expect("nav .o_menu_toggle").toBeVisible();
|
|
expect(".o_action_manager").toHaveInnerHTML("");
|
|
expect(router.current).toEqual({
|
|
action: "__test__client__action__",
|
|
menu_id: 1,
|
|
actionStack: [
|
|
{
|
|
action: "__test__client__action__",
|
|
},
|
|
],
|
|
});
|
|
await animationFrame();
|
|
expect.verifyErrors(["my error"]);
|
|
});
|
|
|
|
test("Apps are reordered at startup based on session's user settings", async () => {
|
|
// Config is written with apps xmlids order (default is menu_1, menu_2)
|
|
patchWithCleanup(user, {
|
|
get settings() {
|
|
return { id: 1, homemenu_config: '["menu_2","menu_1"]' };
|
|
},
|
|
});
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
|
|
const apps = queryAll(".o_app");
|
|
expect(apps[0]).toHaveAttribute("data-menu-xmlid", "menu_2", {
|
|
message: "first displayed app has menu_2 xmlid",
|
|
});
|
|
expect(apps[1]).toHaveAttribute("data-menu-xmlid", "menu_1", {
|
|
message: "second displayed app has menu_1 xmlid",
|
|
});
|
|
expect(apps[0]).toHaveText("App2", { message: "first displayed app is App2" });
|
|
expect(apps[1]).toHaveText("App1", { message: "second displayed app is App1" });
|
|
});
|
|
|
|
test.tags("desktop");
|
|
test("Share URL item is present in the user menu when running as PWA", async () => {
|
|
mockMatchMedia({ ["display-mode"]: "standalone" });
|
|
clearRegistry(registry.category("user_menuitems"));
|
|
registry.category("user_menuitems").add("share_url", shareUrlMenuItem);
|
|
|
|
await mountWithCleanup(UserMenu);
|
|
await contains(".o_user_menu button").click();
|
|
|
|
expect(".o-dropdown--menu .dropdown-item").toHaveCount(1);
|
|
expect(".o-dropdown--menu .dropdown-item").toHaveText("Share");
|
|
});
|
|
|
|
test.tags("desktop");
|
|
test("Share URL item is not present in the user menu when not running as PWA", async () => {
|
|
mockMatchMedia({ ["display-mode"]: "browser" });
|
|
clearRegistry(registry.category("user_menuitems"));
|
|
registry.category("user_menuitems").add("share_url", shareUrlMenuItem);
|
|
|
|
await mountWithCleanup(UserMenu);
|
|
await contains(".o_user_menu button").click();
|
|
|
|
expect(".o-dropdown--menu .dropdown-item").not.toHaveCount();
|
|
});
|
|
|
|
test("Navigate to an application from the HomeMenu should generate only one pushState", async () => {
|
|
patchWithCleanup(history, {
|
|
pushState(state, title, url) {
|
|
super.pushState(...arguments);
|
|
const parsedUrl = new URL(url);
|
|
expect.step(parsedUrl.pathname + parsedUrl.search);
|
|
},
|
|
});
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
|
|
await contains(".o_apps > .o_draggable:nth-child(2) > .o_app").click();
|
|
await animationFrame();
|
|
expect(".test_client_action").toHaveCount(1);
|
|
expect(".test_client_action").toHaveText("ClientAction_Id 2");
|
|
|
|
await goToHomeMenu();
|
|
expect(".o_home_menu").toHaveCount(1);
|
|
|
|
await contains(".o_apps > .o_draggable:nth-child(1) > .o_app").click();
|
|
await animationFrame();
|
|
expect(".test_client_action").toHaveCount(1);
|
|
expect(".test_client_action").toHaveText("ClientAction_Id 1");
|
|
|
|
await goToHomeMenu();
|
|
await animationFrame();
|
|
expect(".o_home_menu").toHaveCount(1);
|
|
expect.verifySteps(["/odoo", "/odoo/action-1002", "/odoo", "/odoo/action-1001", "/odoo"]);
|
|
});
|
|
|
|
test("display studio icon when studio module is not installed", async () => {
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
|
|
expect(`.o_menu_systray .o_nav_entry i`).toHaveClass("oi oi-studio");
|
|
await contains(".o_menu_systray .o_nav_entry").click();
|
|
expect(`.modal-content`).toHaveCount(1);
|
|
expect(queryFirst(".modal-header").textContent).toBe(
|
|
"Odoo Studio - Add new fields to any view"
|
|
);
|
|
});
|
|
|
|
test("studio icon should not be visible for non-admin users", async () => {
|
|
patchWithCleanup(user, {
|
|
isSystem: false,
|
|
});
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
expect(".o_menu_systray .o_nav_entry i").not.toHaveCount();
|
|
});
|
|
|
|
test.tags("desktop");
|
|
test("Should not crash when opening an app via palette and immediately entering input in the palette search", async () => {
|
|
await mountWebClient({ WebClient: WebClientEnterprise });
|
|
|
|
const def = new Deferred();
|
|
onRpc("web_search_read", () => def);
|
|
await keyDown("a");
|
|
await animationFrame();
|
|
await keyDown("Enter");
|
|
await keyDown("a");
|
|
await animationFrame();
|
|
def.resolve();
|
|
await animationFrame();
|
|
expect(".test_client_action").toHaveCount(1);
|
|
expect(".test_client_action").toHaveText("ClientAction_Id 1");
|
|
});
|