529 lines
24 KiB
JavaScript
529 lines
24 KiB
JavaScript
/** @odoo-module **/
|
|
|
|
import {
|
|
click,
|
|
editInput,
|
|
getFixture,
|
|
nextTick,
|
|
patchWithCleanup,
|
|
} from "@web/../tests/helpers/utils";
|
|
import { doAction, getActionManagerServerData, loadState } from "@web/../tests/webclient/helpers";
|
|
import { registry } from "@web/core/registry";
|
|
import { editView } from "@web/views/debug_items";
|
|
import { createEnterpriseWebClient } from "@web_enterprise/../tests/helpers";
|
|
import { homeMenuService } from "@web_enterprise/webclient/home_menu/home_menu_service";
|
|
import { ormService } from "@web/core/orm_service";
|
|
import { enterpriseSubscriptionService } from "@web_enterprise/webclient/home_menu/enterprise_subscription_service";
|
|
import { registerCleanup } from "@web/../tests/helpers/cleanup";
|
|
import { errorService } from "@web/core/errors/error_service";
|
|
import { browser } from "@web/core/browser/browser";
|
|
|
|
const { Component, xml } = owl;
|
|
|
|
let serverData;
|
|
let fixture;
|
|
const serviceRegistry = registry.category("services");
|
|
|
|
// Should test ONLY the webClient and features present in Enterprise
|
|
// Those tests rely on hidden view to be in CSS: display: none
|
|
QUnit.module("WebClient Enterprise", (hooks) => {
|
|
hooks.beforeEach(() => {
|
|
serverData = getActionManagerServerData();
|
|
fixture = getFixture();
|
|
serviceRegistry.add("home_menu", homeMenuService);
|
|
serviceRegistry.add("orm", ormService);
|
|
serviceRegistry.add("enterprise_subscription", enterpriseSubscriptionService);
|
|
});
|
|
|
|
QUnit.module("basic flow with home menu", (hooks) => {
|
|
let mockRPC;
|
|
hooks.beforeEach((assert) => {
|
|
serverData.menus[1].actionID = 4;
|
|
serverData.menus.root.children = [1];
|
|
serverData.views["partner,false,form"] = `
|
|
<form>
|
|
<field name="display_name"/>
|
|
<field name="m2o" open_target="current"/>
|
|
</form>`;
|
|
mockRPC = async (route) => {
|
|
assert.step(route);
|
|
if (route === "/web/dataset/call_kw/partner/get_formview_action") {
|
|
return {
|
|
type: "ir.actions.act_window",
|
|
res_model: "partner",
|
|
view_type: "form",
|
|
view_mode: "form",
|
|
views: [[false, "form"]],
|
|
target: "current",
|
|
res_id: 2,
|
|
};
|
|
}
|
|
};
|
|
});
|
|
|
|
QUnit.test("1 -- start up", async function (assert) {
|
|
await createEnterpriseWebClient({ fixture, serverData, mockRPC });
|
|
assert.verifySteps(["/web/webclient/load_menus"]);
|
|
assert.ok(document.body.classList.contains("o_home_menu_background"));
|
|
assert.containsOnce(fixture, ".o_home_menu");
|
|
assert.isNotVisible(fixture.querySelector(".o_menu_toggle"));
|
|
assert.containsOnce(fixture, ".o_app.o_menuitem");
|
|
});
|
|
|
|
QUnit.test("2 -- navbar updates on displaying an action", async function (assert) {
|
|
await createEnterpriseWebClient({ fixture, serverData, mockRPC });
|
|
assert.verifySteps(["/web/webclient/load_menus"]);
|
|
await click(fixture.querySelector(".o_app.o_menuitem"));
|
|
assert.verifySteps([
|
|
"/web/action/load",
|
|
"/web/dataset/call_kw/partner/get_views",
|
|
"/web/dataset/call_kw/partner/web_search_read",
|
|
]);
|
|
assert.notOk(document.body.classList.contains("o_home_menu_background"));
|
|
assert.containsNone(fixture, ".o_home_menu");
|
|
assert.containsOnce(fixture, ".o_kanban_view");
|
|
const menuToggle = fixture.querySelector(".o_menu_toggle");
|
|
assert.isVisible(menuToggle);
|
|
assert.notOk(menuToggle.classList.contains("o_menu_toggle_back"));
|
|
});
|
|
|
|
QUnit.test("3 -- push another action in the breadcrumb", async function (assert) {
|
|
await createEnterpriseWebClient({ fixture, serverData, mockRPC });
|
|
assert.verifySteps(["/web/webclient/load_menus"]);
|
|
await click(fixture.querySelector(".o_app.o_menuitem"));
|
|
assert.verifySteps([
|
|
"/web/action/load",
|
|
"/web/dataset/call_kw/partner/get_views",
|
|
"/web/dataset/call_kw/partner/web_search_read",
|
|
]);
|
|
await click(fixture.querySelector(".o_kanban_record"));
|
|
await nextTick(); // there is another tick to update navbar and destroy HomeMenu
|
|
assert.verifySteps(["/web/dataset/call_kw/partner/read"]);
|
|
assert.isVisible(fixture.querySelector(".o_menu_toggle"));
|
|
assert.containsOnce(fixture, ".o_form_view");
|
|
assert.strictEqual(
|
|
fixture.querySelector(".breadcrumb-item.active").textContent,
|
|
"First record"
|
|
);
|
|
});
|
|
|
|
QUnit.test("4 -- push a third action in the breadcrumb", async function (assert) {
|
|
await createEnterpriseWebClient({ fixture, serverData, mockRPC });
|
|
assert.verifySteps(["/web/webclient/load_menus"]);
|
|
await click(fixture.querySelector(".o_app.o_menuitem"));
|
|
assert.verifySteps([
|
|
"/web/action/load",
|
|
"/web/dataset/call_kw/partner/get_views",
|
|
"/web/dataset/call_kw/partner/web_search_read",
|
|
]);
|
|
await click(fixture.querySelector(".o_kanban_record"));
|
|
assert.verifySteps(["/web/dataset/call_kw/partner/read"]);
|
|
await click(fixture, '.o_field_widget[name="m2o"] .o_external_button', true);
|
|
assert.verifySteps([
|
|
"/web/dataset/call_kw/partner/get_formview_action",
|
|
"/web/dataset/call_kw/partner/get_views",
|
|
"/web/dataset/call_kw/partner/read",
|
|
]);
|
|
assert.containsOnce(fixture, ".o_form_view");
|
|
assert.strictEqual(
|
|
fixture.querySelector(".breadcrumb-item.active").textContent,
|
|
"Second record"
|
|
);
|
|
assert.containsN(fixture, ".breadcrumb-item", 3);
|
|
});
|
|
|
|
QUnit.test(
|
|
"5 -- switch to HomeMenu from an action with 2 breadcrumbs",
|
|
async function (assert) {
|
|
await createEnterpriseWebClient({ fixture, serverData, mockRPC });
|
|
assert.verifySteps(["/web/webclient/load_menus"]);
|
|
await click(fixture.querySelector(".o_app.o_menuitem"));
|
|
assert.verifySteps([
|
|
"/web/action/load",
|
|
"/web/dataset/call_kw/partner/get_views",
|
|
"/web/dataset/call_kw/partner/web_search_read",
|
|
]);
|
|
await click(fixture.querySelector(".o_kanban_record"));
|
|
assert.verifySteps(["/web/dataset/call_kw/partner/read"]);
|
|
await click(fixture, '.o_field_widget[name="m2o"] .o_external_button', true);
|
|
assert.verifySteps([
|
|
"/web/dataset/call_kw/partner/get_formview_action",
|
|
"/web/dataset/call_kw/partner/get_views",
|
|
"/web/dataset/call_kw/partner/read",
|
|
]);
|
|
const menuToggle = fixture.querySelector(".o_menu_toggle");
|
|
await click(menuToggle);
|
|
assert.verifySteps([]);
|
|
assert.ok(menuToggle.classList.contains("o_menu_toggle_back"));
|
|
assert.containsOnce(fixture, ".o_home_menu");
|
|
assert.isNotVisible(fixture.querySelector(".o_form_view"));
|
|
}
|
|
);
|
|
|
|
QUnit.test("6 -- back to underlying action with many breadcrumbs", async function (assert) {
|
|
await createEnterpriseWebClient({ fixture, serverData, mockRPC });
|
|
assert.verifySteps(["/web/webclient/load_menus"]);
|
|
await click(fixture.querySelector(".o_app.o_menuitem"));
|
|
assert.verifySteps([
|
|
"/web/action/load",
|
|
"/web/dataset/call_kw/partner/get_views",
|
|
"/web/dataset/call_kw/partner/web_search_read",
|
|
]);
|
|
await click(fixture.querySelector(".o_kanban_record"));
|
|
assert.verifySteps(["/web/dataset/call_kw/partner/read"]);
|
|
await click(fixture, '.o_field_widget[name="m2o"] .o_external_button', true);
|
|
assert.verifySteps([
|
|
"/web/dataset/call_kw/partner/get_formview_action",
|
|
"/web/dataset/call_kw/partner/get_views",
|
|
"/web/dataset/call_kw/partner/read",
|
|
]);
|
|
const menuToggle = fixture.querySelector(".o_menu_toggle");
|
|
await click(menuToggle);
|
|
await click(menuToggle);
|
|
// if we don't reload on going back to underlying action
|
|
// assert.verifySteps(
|
|
// [],
|
|
// "the underlying view should not reload when toggling the HomeMenu to off"
|
|
// );
|
|
// endif
|
|
// if we reload on going back to underlying action
|
|
assert.verifySteps(
|
|
["/web/dataset/call_kw/partner/read"],
|
|
"the underlying view should reload when toggling the HomeMenu to off"
|
|
);
|
|
// endif
|
|
assert.containsNone(fixture, ".o_home_menu");
|
|
assert.containsOnce(fixture, ".o_form_view");
|
|
assert.notOk(menuToggle.classList.contains("o_menu_toggle_back"));
|
|
assert.strictEqual(
|
|
fixture.querySelector(".breadcrumb-item.active").textContent,
|
|
"Second record"
|
|
);
|
|
assert.containsN(fixture, ".breadcrumb-item", 3);
|
|
});
|
|
|
|
QUnit.test("restore the newly created record in form view (legacy)", async (assert) => {
|
|
const action = serverData.actions[6];
|
|
delete action.res_id;
|
|
action.target = "current";
|
|
const webClient = await createEnterpriseWebClient({ fixture, serverData });
|
|
|
|
await doAction(webClient, 6);
|
|
assert.containsOnce(fixture, ".o_form_view");
|
|
assert.containsOnce(fixture, ".o_form_view .o_form_editable");
|
|
await editInput(fixture, ".o_field_widget[name=display_name] input", "red right hand");
|
|
await click(fixture.querySelector(".o_form_button_save"));
|
|
assert.strictEqual(
|
|
fixture.querySelector(".breadcrumb-item.active").textContent,
|
|
"red right hand"
|
|
);
|
|
await click(fixture.querySelector(".o_menu_toggle"));
|
|
assert.isNotVisible(fixture.querySelector(".o_form_view"));
|
|
|
|
await click(fixture.querySelector(".o_menu_toggle"));
|
|
assert.containsOnce(fixture, ".o_form_view");
|
|
assert.containsOnce(fixture, ".o_form_view .o_form_saved");
|
|
assert.strictEqual(
|
|
fixture.querySelector(".breadcrumb-item.active").textContent,
|
|
"red right hand"
|
|
);
|
|
});
|
|
|
|
QUnit.skip("fast clicking on restore (implementation detail)", async (assert) => {
|
|
assert.expect(6);
|
|
|
|
let doVeryFastClick = false;
|
|
|
|
class DelayedClientAction extends Component {
|
|
setup() {
|
|
owl.onMounted(() => {
|
|
if (doVeryFastClick) {
|
|
doVeryFastClick = false;
|
|
click(fixture.querySelector(".o_menu_toggle"));
|
|
}
|
|
});
|
|
}
|
|
}
|
|
DelayedClientAction.template = xml`<div class='delayed_client_action'>
|
|
<button t-on-click="resolve">RESOLVE</button>
|
|
</div>`;
|
|
|
|
registry.category("actions").add("DelayedClientAction", DelayedClientAction);
|
|
const webClient = await createEnterpriseWebClient({ fixture, serverData });
|
|
await doAction(webClient, "DelayedClientAction");
|
|
await nextTick();
|
|
await click(fixture.querySelector(".o_menu_toggle"));
|
|
assert.isVisible(fixture.querySelector(".o_home_menu"));
|
|
assert.isNotVisible(fixture.querySelector(".delayed_client_action"));
|
|
|
|
doVeryFastClick = true;
|
|
await click(fixture.querySelector(".o_menu_toggle"));
|
|
await nextTick();
|
|
// off homemenu
|
|
assert.isVisible(fixture.querySelector(".o_home_menu"));
|
|
assert.isNotVisible(fixture.querySelector(".delayed_client_action"));
|
|
|
|
await click(fixture.querySelector(".o_menu_toggle"));
|
|
await nextTick();
|
|
assert.containsNone(fixture, ".o_home_menu");
|
|
assert.containsOnce(fixture, ".delayed_client_action");
|
|
});
|
|
});
|
|
|
|
QUnit.test("clear unCommittedChanges when toggling home menu", async function (assert) {
|
|
assert.expect(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
|
|
|
|
const mockRPC = (route, args) => {
|
|
if (args.method === "create") {
|
|
assert.strictEqual(args.model, "partner");
|
|
assert.deepEqual(args.args, [
|
|
{
|
|
display_name: "red right hand",
|
|
foo: false,
|
|
},
|
|
]);
|
|
}
|
|
};
|
|
|
|
const webClient = await createEnterpriseWebClient({ fixture, serverData, mockRPC });
|
|
await doAction(webClient, 3, { viewType: "form" });
|
|
assert.containsOnce(fixture, ".o_form_view .o_form_editable");
|
|
await editInput(fixture, ".o_field_widget[name=display_name] input", "red right hand");
|
|
|
|
await click(fixture.querySelector(".o_menu_toggle"));
|
|
assert.containsNone(fixture, ".o_form_view");
|
|
assert.containsNone(fixture, ".modal");
|
|
assert.containsOnce(fixture, ".o_home_menu");
|
|
});
|
|
|
|
QUnit.test("can have HomeMenu and dialog action", async function (assert) {
|
|
const webClient = await createEnterpriseWebClient({ fixture, serverData });
|
|
assert.containsOnce(fixture, ".o_home_menu");
|
|
assert.containsNone(fixture, ".modal .o_form_view");
|
|
await doAction(webClient, 5);
|
|
assert.containsOnce(fixture, ".modal .o_form_view");
|
|
assert.isVisible(fixture.querySelector(".modal .o_form_view"));
|
|
assert.containsOnce(fixture, ".o_home_menu");
|
|
});
|
|
|
|
QUnit.test("supports attachments of apps deleted", async function (assert) {
|
|
// When doing a pg_restore without the filestore
|
|
// LPE fixme: may not be necessary anymore since menus are not HomeMenu props anymore
|
|
serverData.menus = {
|
|
root: { id: "root", children: [1], name: "root", appID: "root" },
|
|
1: {
|
|
id: 1,
|
|
appID: 1,
|
|
actionID: 1,
|
|
xmlid: "",
|
|
name: "Partners",
|
|
children: [],
|
|
webIconData: "",
|
|
webIcon: "bloop,bloop",
|
|
},
|
|
};
|
|
patchWithCleanup(odoo, { debug: "1" });
|
|
await createEnterpriseWebClient({ fixture, serverData });
|
|
assert.containsOnce(fixture, ".o_home_menu");
|
|
});
|
|
|
|
QUnit.test(
|
|
"debug manager resets to global items when home menu is displayed",
|
|
async function (assert) {
|
|
const debugRegistry = registry.category("debug");
|
|
debugRegistry.category("view").add("editView", editView);
|
|
debugRegistry.category("default").add("item_1", () => {
|
|
return {
|
|
type: "item",
|
|
description: "globalItem",
|
|
callback: () => {},
|
|
sequence: 10,
|
|
};
|
|
});
|
|
const mockRPC = async (route) => {
|
|
if (route.includes("check_access_rights")) {
|
|
return true;
|
|
}
|
|
};
|
|
patchWithCleanup(odoo, { debug: "1" });
|
|
const webClient = await createEnterpriseWebClient({ fixture, serverData, mockRPC });
|
|
await click(fixture.querySelector(".o_debug_manager .dropdown-toggle"));
|
|
assert.containsOnce(fixture, ".o_debug_manager .dropdown-item:contains('globalItem')");
|
|
assert.containsNone(
|
|
fixture,
|
|
".o_debug_manager .dropdown-item:contains('Edit View: Kanban')"
|
|
);
|
|
await click(fixture.querySelector(".o_debug_manager .dropdown-toggle"));
|
|
await doAction(webClient, 1);
|
|
await click(fixture.querySelector(".o_debug_manager .dropdown-toggle"));
|
|
assert.containsOnce(fixture, ".o_debug_manager .dropdown-item:contains('globalItem')");
|
|
assert.containsOnce(
|
|
fixture,
|
|
".o_debug_manager .dropdown-item:contains('Edit View: Kanban')"
|
|
);
|
|
await click(fixture.querySelector(".o_menu_toggle"));
|
|
await click(fixture.querySelector(".o_debug_manager .dropdown-toggle"));
|
|
assert.containsOnce(fixture, ".o_debug_manager .dropdown-item:contains('globalItem')");
|
|
assert.containsNone(
|
|
fixture,
|
|
".o_debug_manager .dropdown-item:contains('Edit View: Kanban')"
|
|
);
|
|
await click(fixture.querySelector(".o_debug_manager .dropdown-toggle"));
|
|
await doAction(webClient, 3);
|
|
await click(fixture.querySelector(".o_debug_manager .dropdown-toggle"));
|
|
assert.containsOnce(fixture, ".o_debug_manager .dropdown-item:contains('globalItem')");
|
|
assert.containsOnce(
|
|
fixture,
|
|
".o_debug_manager .dropdown-item:contains('Edit View: List')"
|
|
);
|
|
assert.containsNone(
|
|
fixture,
|
|
".o_debug_manager .dropdown-item:contains('Edit View: Kanban')"
|
|
);
|
|
}
|
|
);
|
|
|
|
QUnit.test(
|
|
"url state is well handled when going in and out of the HomeMenu",
|
|
async function (assert) {
|
|
const webClient = await createEnterpriseWebClient({ fixture, serverData });
|
|
await nextTick();
|
|
assert.deepEqual(webClient.env.services.router.current.hash, { action: "menu" });
|
|
|
|
await click(fixture.querySelector(".o_app.o_menuitem:nth-child(2)"));
|
|
await nextTick();
|
|
assert.deepEqual(webClient.env.services.router.current.hash, {
|
|
action: 1002,
|
|
menu_id: 2,
|
|
});
|
|
|
|
await click(fixture.querySelector(".o_menu_toggle"));
|
|
await nextTick();
|
|
assert.deepEqual(webClient.env.services.router.current.hash, { action: "menu" });
|
|
|
|
await click(fixture.querySelector(".o_menu_toggle"));
|
|
await nextTick();
|
|
// if we reload on going back to underlying action
|
|
// end if
|
|
assert.deepEqual(webClient.env.services.router.current.hash, {
|
|
action: 1002,
|
|
menu_id: 2,
|
|
});
|
|
}
|
|
);
|
|
|
|
QUnit.test(
|
|
"underlying action's menu items are invisible when HomeMenu is displayed",
|
|
async function (assert) {
|
|
serverData.menus[1].children = [99];
|
|
serverData.menus[99] = {
|
|
id: 99,
|
|
children: [],
|
|
name: "SubMenu",
|
|
appID: 1,
|
|
actionID: 1002,
|
|
xmlid: "",
|
|
webIconData: undefined,
|
|
webIcon: false,
|
|
};
|
|
await createEnterpriseWebClient({ fixture, serverData });
|
|
assert.containsNone(fixture, "nav .o_menu_sections");
|
|
assert.containsNone(fixture, "nav .o_menu_brand");
|
|
await click(fixture.querySelector(".o_app.o_menuitem:nth-child(1)"));
|
|
await nextTick();
|
|
assert.containsOnce(fixture, "nav .o_menu_sections");
|
|
assert.containsOnce(fixture, "nav .o_menu_brand");
|
|
assert.isVisible(fixture.querySelector(".o_menu_sections"));
|
|
assert.isVisible(fixture.querySelector(".o_menu_brand"));
|
|
await click(fixture.querySelector(".o_menu_toggle"));
|
|
assert.containsOnce(fixture, "nav .o_menu_sections");
|
|
assert.containsOnce(fixture, "nav .o_menu_brand");
|
|
assert.isNotVisible(fixture.querySelector(".o_menu_sections"));
|
|
assert.isNotVisible(fixture.querySelector(".o_menu_brand"));
|
|
}
|
|
);
|
|
|
|
QUnit.test("loadState back and forth keeps relevant keys in state", async function (assert) {
|
|
const webClient = await createEnterpriseWebClient({ fixture, serverData });
|
|
|
|
await click(fixture.querySelector(".o_app.o_menuitem:nth-child(2)"));
|
|
await nextTick();
|
|
assert.containsOnce(fixture, ".test_client_action");
|
|
assert.containsNone(fixture, ".o_home_menu");
|
|
const state = webClient.env.services.router.current.hash;
|
|
assert.deepEqual(state, {
|
|
action: 1002,
|
|
menu_id: 2,
|
|
});
|
|
|
|
await loadState(webClient, {});
|
|
assert.containsNone(fixture, ".test_client_action");
|
|
assert.containsOnce(fixture, ".o_home_menu");
|
|
assert.deepEqual(webClient.env.services.router.current.hash, {
|
|
action: "menu",
|
|
});
|
|
|
|
await loadState(webClient, state);
|
|
assert.containsOnce(fixture, ".test_client_action");
|
|
assert.containsNone(fixture, ".o_home_menu");
|
|
assert.deepEqual(webClient.env.services.router.current.hash, state);
|
|
});
|
|
|
|
QUnit.test(
|
|
"go back to home menu using browser back button (i.e. loadState)",
|
|
async function (assert) {
|
|
const webClient = await createEnterpriseWebClient({ fixture, serverData });
|
|
assert.containsOnce(fixture, ".o_home_menu");
|
|
assert.isNotVisible(fixture.querySelector(".o_main_navbar .o_menu_toggle"));
|
|
|
|
await click(fixture.querySelector(".o_app.o_menuitem:nth-child(2)"));
|
|
assert.containsOnce(fixture, ".test_client_action");
|
|
assert.containsNone(fixture, ".o_home_menu");
|
|
|
|
await loadState(webClient, { action: "menu" }); // FIXME: this might need to be changed
|
|
assert.containsNone(fixture, ".test_client_action");
|
|
assert.containsOnce(fixture, ".o_home_menu");
|
|
assert.isNotVisible(fixture.querySelector(".o_main_navbar .o_menu_toggle"));
|
|
}
|
|
);
|
|
|
|
QUnit.test("initial action crashes", async (assert) => {
|
|
const handler = (ev) => {
|
|
// need to preventDefault to remove error from console (so python test pass)
|
|
ev.preventDefault();
|
|
};
|
|
window.addEventListener("unhandledrejection", handler);
|
|
registerCleanup(() => window.removeEventListener("unhandledrejection", handler));
|
|
|
|
patchWithCleanup(QUnit, {
|
|
onUnhandledRejection: () => {},
|
|
});
|
|
|
|
browser.location.hash = "#action=__test__client__action__&menu_id=1";
|
|
const ClientAction = registry.category("actions").get("__test__client__action__");
|
|
class Override extends ClientAction {
|
|
setup() {
|
|
super.setup();
|
|
assert.step("clientAction setup");
|
|
throw new Error("my error");
|
|
}
|
|
}
|
|
registry.category("actions").add("__test__client__action__", Override, { force: true });
|
|
|
|
registry.category("services").add("error", errorService);
|
|
|
|
const webClient = await createEnterpriseWebClient({ fixture, serverData });
|
|
assert.verifySteps(["clientAction setup"]);
|
|
assert.containsOnce(fixture, "nav .o_menu_toggle");
|
|
assert.isVisible(fixture.querySelector("nav .o_menu_toggle"));
|
|
assert.strictEqual(fixture.querySelector(".o_action_manager").innerHTML, "");
|
|
assert.deepEqual(webClient.env.services.router.current.hash, {
|
|
action: "__test__client__action__",
|
|
menu_id: 1,
|
|
});
|
|
});
|
|
});
|