mirror of
https://github.com/bringout/oca-ocb-core.git
synced 2026-04-20 00:32:03 +02:00
vanilla 17.0
This commit is contained in:
parent
d72e748793
commit
a9bcec8e91
1986 changed files with 1613876 additions and 568976 deletions
|
|
@ -2,20 +2,11 @@
|
|||
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
import { registry } from "@web/core/registry";
|
||||
import core from "web.core";
|
||||
import AbstractAction from "web.AbstractAction";
|
||||
import testUtils from "web.test_utils";
|
||||
import { registerCleanup } from "../../helpers/cleanup";
|
||||
import {
|
||||
click,
|
||||
getFixture,
|
||||
legacyExtraNextTick,
|
||||
nextTick,
|
||||
patchWithCleanup,
|
||||
} from "../../helpers/utils";
|
||||
import testUtils from "@web/../tests/legacy/helpers/test_utils";
|
||||
import { click, getFixture, nextTick, patchWithCleanup } from "../../helpers/utils";
|
||||
import { createWebClient, doAction, getActionManagerServerData } from "./../helpers";
|
||||
|
||||
import { Component, xml } from "@odoo/owl";
|
||||
import { Component, onMounted, xml } from "@odoo/owl";
|
||||
|
||||
let serverData;
|
||||
let target;
|
||||
|
|
@ -106,41 +97,39 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
assert.verifySteps(["web_search_read"]);
|
||||
});
|
||||
|
||||
QUnit.test("soft_reload a form view", async (assert) => {
|
||||
const mockRPC = async function (route, { args }) {
|
||||
if (route === "/web/dataset/call_kw/partner/web_read") {
|
||||
assert.step(`read ${args[0][0]}`);
|
||||
}
|
||||
};
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
await doAction(webClient, {
|
||||
name: "Partners",
|
||||
res_model: "partner",
|
||||
views: [
|
||||
[false, "list"],
|
||||
[false, "form"],
|
||||
],
|
||||
type: "ir.actions.act_window",
|
||||
});
|
||||
|
||||
await click(target.querySelector(".o_data_row .o_data_cell"));
|
||||
await click(target, ".o_form_view .o_pager_next");
|
||||
assert.verifySteps([
|
||||
"read 1",
|
||||
"read 2",
|
||||
])
|
||||
await doAction(webClient, "soft_reload");
|
||||
assert.verifySteps(["read 2"]);
|
||||
});
|
||||
|
||||
QUnit.test("soft_reload when there is no controller", async (assert) => {
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, "soft_reload");
|
||||
assert.ok(true, "No ControllerNotFoundError when there is no controller to restore");
|
||||
});
|
||||
|
||||
QUnit.test("can execute client actions from tag name (legacy)", async function (assert) {
|
||||
// remove this test as soon as legacy Widgets are no longer supported
|
||||
assert.expect(4);
|
||||
const ClientAction = AbstractAction.extend({
|
||||
start: function () {
|
||||
this.$el.text("Hello World");
|
||||
this.$el.addClass("o_client_action_test");
|
||||
},
|
||||
});
|
||||
const mockRPC = async function (route, args) {
|
||||
assert.step((args && args.method) || route);
|
||||
};
|
||||
core.action_registry.add("HelloWorldTestLeg", ClientAction);
|
||||
registerCleanup(() => delete core.action_registry.map.HelloWorldTestLeg);
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
await doAction(webClient, "HelloWorldTestLeg");
|
||||
assert.containsNone(
|
||||
document.body,
|
||||
".o_control_panel",
|
||||
"shouldn't have rendered a control panel"
|
||||
);
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_client_action_test").text(),
|
||||
"Hello World",
|
||||
"should have correctly rendered the client action"
|
||||
);
|
||||
assert.verifySteps(["/web/webclient/load_menus"]);
|
||||
});
|
||||
|
||||
QUnit.test("can execute client actions from tag name", async function (assert) {
|
||||
assert.expect(4);
|
||||
class ClientAction extends Component {}
|
||||
|
|
@ -196,234 +185,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
}
|
||||
);
|
||||
|
||||
QUnit.test("client action with control panel (legacy)", async function (assert) {
|
||||
assert.expect(4);
|
||||
// LPE Fixme: at this time we don't really know the API that wowl ClientActions implement
|
||||
const ClientAction = AbstractAction.extend({
|
||||
hasControlPanel: true,
|
||||
start() {
|
||||
this.$(".o_content").text("Hello World");
|
||||
this.$el.addClass("o_client_action_test");
|
||||
this.controlPanelProps.title = "Hello";
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
core.action_registry.add("HelloWorldTest", ClientAction);
|
||||
registerCleanup(() => delete core.action_registry.map.HelloWorldTest);
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, "HelloWorldTest");
|
||||
assert.strictEqual(
|
||||
$(".o_control_panel:visible").length,
|
||||
1,
|
||||
"should have rendered a control panel"
|
||||
);
|
||||
assert.containsN(
|
||||
target,
|
||||
".o_control_panel .breadcrumb-item",
|
||||
1,
|
||||
"there should be one controller in the breadcrumbs"
|
||||
);
|
||||
assert.strictEqual(
|
||||
$(".o_control_panel .breadcrumb-item").text(),
|
||||
"Hello",
|
||||
"breadcrumbs should still display the title of the controller"
|
||||
);
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_client_action_test .o_content").text(),
|
||||
"Hello World",
|
||||
"should have correctly rendered the client action"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("state is pushed for client action (legacy)", async function (assert) {
|
||||
assert.expect(6);
|
||||
const ClientAction = AbstractAction.extend({
|
||||
getTitle: function () {
|
||||
return "a title";
|
||||
},
|
||||
getState: function () {
|
||||
return { foo: "baz" };
|
||||
},
|
||||
});
|
||||
const pushState = browser.history.pushState;
|
||||
patchWithCleanup(browser, {
|
||||
history: Object.assign({}, browser.history, {
|
||||
pushState() {
|
||||
pushState(...arguments);
|
||||
assert.step("push_state");
|
||||
},
|
||||
}),
|
||||
});
|
||||
|
||||
core.action_registry.add("HelloWorldTest", ClientAction);
|
||||
registerCleanup(() => delete core.action_registry.map.HelloWorldTest);
|
||||
const webClient = await createWebClient({ serverData });
|
||||
let currentTitle = webClient.env.services.title.current;
|
||||
assert.strictEqual(currentTitle, '{"zopenerp":"Odoo"}');
|
||||
let currentHash = webClient.env.services.router.current.hash;
|
||||
assert.deepEqual(currentHash, {});
|
||||
await doAction(webClient, "HelloWorldTest");
|
||||
currentTitle = webClient.env.services.title.current;
|
||||
assert.strictEqual(currentTitle, '{"zopenerp":"Odoo","action":"a title"}');
|
||||
currentHash = webClient.env.services.router.current.hash;
|
||||
assert.deepEqual(currentHash, {
|
||||
action: "HelloWorldTest",
|
||||
foo: "baz",
|
||||
});
|
||||
assert.verifySteps(["push_state"]);
|
||||
});
|
||||
|
||||
QUnit.test("action can use a custom control panel (legacy)", async function (assert) {
|
||||
assert.expect(1);
|
||||
class CustomControlPanel extends Component {}
|
||||
CustomControlPanel.template = xml`
|
||||
<div class="custom-control-panel">My custom control panel</div>
|
||||
`;
|
||||
const ClientAction = AbstractAction.extend({
|
||||
hasControlPanel: true,
|
||||
config: {
|
||||
ControlPanel: CustomControlPanel,
|
||||
},
|
||||
});
|
||||
core.action_registry.add("HelloWorldTest", ClientAction);
|
||||
registerCleanup(() => delete core.action_registry.map.HelloWorldTest);
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, "HelloWorldTest");
|
||||
assert.containsOnce(target, ".custom-control-panel", "should have a custom control panel");
|
||||
});
|
||||
|
||||
QUnit.test("breadcrumb is updated on title change (legacy)", async function (assert) {
|
||||
assert.expect(2);
|
||||
const ClientAction = AbstractAction.extend({
|
||||
hasControlPanel: true,
|
||||
events: {
|
||||
click: function () {
|
||||
this.updateControlPanel({ title: "new title" });
|
||||
},
|
||||
},
|
||||
start: async function () {
|
||||
this.$(".o_content").text("Hello World");
|
||||
this.$el.addClass("o_client_action_test");
|
||||
this.controlPanelProps.title = "initial title";
|
||||
await this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
core.action_registry.add("HelloWorldTest", ClientAction);
|
||||
registerCleanup(() => delete core.action_registry.map.HelloWorldTest);
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, "HelloWorldTest");
|
||||
assert.strictEqual(
|
||||
$("ol.breadcrumb").text(),
|
||||
"initial title",
|
||||
"should have initial title as breadcrumb content"
|
||||
);
|
||||
await testUtils.dom.click($(target).find(".o_client_action_test"));
|
||||
await legacyExtraNextTick();
|
||||
assert.strictEqual(
|
||||
$("ol.breadcrumb").text(),
|
||||
"new title",
|
||||
"should have updated title as breadcrumb content"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("client actions can have breadcrumbs (legacy)", async function (assert) {
|
||||
assert.expect(4);
|
||||
const ClientAction = AbstractAction.extend({
|
||||
hasControlPanel: true,
|
||||
init(parent, action) {
|
||||
action.display_name = "Goldeneye";
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
start() {
|
||||
this.$el.addClass("o_client_action_test");
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
const ClientAction2 = AbstractAction.extend({
|
||||
hasControlPanel: true,
|
||||
init(parent, action) {
|
||||
action.display_name = "No time for sweetness";
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
start() {
|
||||
this.$el.addClass("o_client_action_test_2");
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
core.action_registry.add("ClientAction", ClientAction);
|
||||
core.action_registry.add("ClientAction2", ClientAction2);
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, "ClientAction");
|
||||
assert.containsOnce(target, ".breadcrumb-item");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".breadcrumb-item.active").textContent,
|
||||
"Goldeneye"
|
||||
);
|
||||
await doAction(webClient, "ClientAction2", { clearBreadcrumbs: false });
|
||||
assert.containsN(target, ".breadcrumb-item", 2);
|
||||
assert.strictEqual(
|
||||
target.querySelector(".breadcrumb-item.active").textContent,
|
||||
"No time for sweetness"
|
||||
);
|
||||
delete core.action_registry.map.ClientAction;
|
||||
delete core.action_registry.map.ClientAction2;
|
||||
});
|
||||
|
||||
QUnit.test("client action restore scrollbar (legacy)", async function (assert) {
|
||||
assert.expect(7);
|
||||
const ClientAction = AbstractAction.extend({
|
||||
hasControlPanel: true,
|
||||
init(parent, action) {
|
||||
action.display_name = "Title1";
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
async start() {
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const content = document.createElement("div");
|
||||
content.innerText = "Paper company";
|
||||
content.className = "lorem";
|
||||
this.el.querySelector(".o_content").appendChild(content);
|
||||
}
|
||||
await this._super(arguments);
|
||||
},
|
||||
});
|
||||
const ClientAction2 = AbstractAction.extend({
|
||||
hasControlPanel: true,
|
||||
init(parent, action) {
|
||||
action.display_name = "Title2";
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
start() {
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
core.action_registry.add("ClientAction", ClientAction);
|
||||
core.action_registry.add("ClientAction2", ClientAction2);
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, "ClientAction");
|
||||
assert.containsOnce(target, ".breadcrumb-item");
|
||||
assert.strictEqual(target.querySelector(".breadcrumb-item.active").textContent, "Title1");
|
||||
|
||||
target.querySelector(".lorem:last-child").scrollIntoView();
|
||||
const scrollPosition = target.querySelector(".o_content").scrollTop;
|
||||
assert.ok(scrollPosition > 0);
|
||||
await doAction(webClient, "ClientAction2", { clearBreadcrumbs: false });
|
||||
assert.containsN(target, ".breadcrumb-item", 2);
|
||||
assert.strictEqual(target.querySelector(".breadcrumb-item.active").textContent, "Title2");
|
||||
|
||||
await click(target.querySelector(".breadcrumb-item:first-child"));
|
||||
assert.strictEqual(target.querySelector(".breadcrumb-item.active").textContent, "Title1");
|
||||
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_content").scrollTop,
|
||||
scrollPosition,
|
||||
"Should restore the scroll"
|
||||
);
|
||||
delete core.action_registry.map.ClientAction;
|
||||
delete core.action_registry.map.ClientAction2;
|
||||
});
|
||||
|
||||
QUnit.test("ClientAction receives breadcrumbs and exports title (wowl)", async (assert) => {
|
||||
QUnit.test("ClientAction receives breadcrumbs and exports title", async (assert) => {
|
||||
assert.expect(4);
|
||||
class ClientAction extends Component {
|
||||
setup() {
|
||||
|
|
@ -431,7 +193,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
const { breadcrumbs } = this.env.config;
|
||||
assert.strictEqual(breadcrumbs.length, 2);
|
||||
assert.strictEqual(breadcrumbs[0].name, "Favorite Ponies");
|
||||
owl.onMounted(() => {
|
||||
onMounted(() => {
|
||||
this.env.config.setDisplayName(this.breadcrumbTitle);
|
||||
});
|
||||
}
|
||||
|
|
@ -449,12 +211,12 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
await click(target, ".my_owl_action");
|
||||
await doAction(webClient, 3);
|
||||
assert.strictEqual(
|
||||
target.querySelector(".breadcrumb").textContent,
|
||||
target.querySelector(".o_breadcrumb").textContent,
|
||||
"Favorite PoniesnewOwlTitlePartners"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("ClientAction receives arbitrary props from doAction (wowl)", async (assert) => {
|
||||
QUnit.test("ClientAction receives arbitrary props from doAction", async (assert) => {
|
||||
assert.expect(1);
|
||||
class ClientAction extends Component {
|
||||
setup() {
|
||||
|
|
@ -469,22 +231,6 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
});
|
||||
});
|
||||
|
||||
QUnit.test("ClientAction receives arbitrary props from doAction (legacy)", async (assert) => {
|
||||
assert.expect(1);
|
||||
const ClientAction = AbstractAction.extend({
|
||||
init(parent, action, options) {
|
||||
assert.strictEqual(options.division, "bell");
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
core.action_registry.add("ClientAction", ClientAction);
|
||||
registerCleanup(() => delete core.action_registry.map.ClientAction);
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, "ClientAction", {
|
||||
props: { division: "bell" },
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test("test display_notification client action", async function (assert) {
|
||||
assert.expect(6);
|
||||
const webClient = await createWebClient({ serverData });
|
||||
|
|
@ -499,6 +245,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
sticky: true,
|
||||
},
|
||||
});
|
||||
await nextTick(); // wait for the notification to be displayed
|
||||
const notificationSelector = ".o_notification_manager .o_notification";
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
|
|
@ -545,6 +292,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
],
|
||||
},
|
||||
});
|
||||
await nextTick(); // wait for the notification to be displayed
|
||||
const notificationSelector = ".o_notification_manager .o_notification";
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
|
|
@ -585,6 +333,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
],
|
||||
},
|
||||
});
|
||||
await nextTick(); // wait for the notification to be displayed
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
notificationSelector,
|
||||
|
|
@ -621,6 +370,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
},
|
||||
options
|
||||
);
|
||||
await nextTick(); // wait for the notification to be displayed
|
||||
const notificationSelector = ".o_notification_manager .o_notification";
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
|
|
@ -629,4 +379,49 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
);
|
||||
assert.verifySteps(["onClose"]);
|
||||
});
|
||||
|
||||
QUnit.test("test reload client action", async function (assert) {
|
||||
patchWithCleanup(browser.location, {
|
||||
assign: (url) => {
|
||||
assert.step(url);
|
||||
},
|
||||
origin: "",
|
||||
hash: "#test=42",
|
||||
});
|
||||
|
||||
const webClient = await createWebClient({ serverData });
|
||||
|
||||
await doAction(webClient, {
|
||||
type: "ir.actions.client",
|
||||
tag: "reload",
|
||||
});
|
||||
await doAction(webClient, {
|
||||
type: "ir.actions.client",
|
||||
tag: "reload",
|
||||
params: {
|
||||
action_id: 2,
|
||||
},
|
||||
});
|
||||
await doAction(webClient, {
|
||||
type: "ir.actions.client",
|
||||
tag: "reload",
|
||||
params: {
|
||||
menu_id: 1,
|
||||
},
|
||||
});
|
||||
await doAction(webClient, {
|
||||
type: "ir.actions.client",
|
||||
tag: "reload",
|
||||
params: {
|
||||
action_id: 1,
|
||||
menu_id: 2,
|
||||
},
|
||||
});
|
||||
assert.verifySteps([
|
||||
"/web/tests?reload=true#test=42",
|
||||
"/web/tests?reload=true#action=2",
|
||||
"/web/tests?reload=true#menu_id=1",
|
||||
"/web/tests?reload=true#menu_id=2&action=1",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,17 +1,9 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import testUtils from "web.test_utils";
|
||||
import { registerCleanup } from "../../helpers/cleanup";
|
||||
import {
|
||||
click,
|
||||
getFixture,
|
||||
legacyExtraNextTick,
|
||||
nextTick,
|
||||
patchWithCleanup,
|
||||
} from "../../helpers/utils";
|
||||
import testUtils from "@web/../tests/legacy/helpers/test_utils";
|
||||
import { click, getFixture, nextTick, patchWithCleanup } from "../../helpers/utils";
|
||||
import { createWebClient, doAction, getActionManagerServerData } from "./../helpers";
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { formView } from "@web/views/form/form_view";
|
||||
import { listView } from "../../../src/views/list/list_view";
|
||||
|
||||
|
|
@ -50,9 +42,9 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
assert.step("on_close");
|
||||
}
|
||||
await doAction(webClient, 5, { onClose });
|
||||
assert.containsOnce(target, ".o_dialog_container .o_dialog");
|
||||
await click(target.querySelector(".o_dialog_container .o_dialog .modal-header button"));
|
||||
assert.containsNone(target, ".o_dialog_container .o_dialog");
|
||||
assert.containsOnce(target, ".o_dialog");
|
||||
await click(target.querySelector(".o_dialog .modal-header button"));
|
||||
assert.containsNone(target, ".o_dialog");
|
||||
assert.verifySteps(["on_close"]);
|
||||
|
||||
// execute an 'ir.actions.act_window_close' action
|
||||
|
|
@ -107,7 +99,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
let form;
|
||||
patchWithCleanup(formView.Controller.prototype, {
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
super.setup(...arguments);
|
||||
form = this;
|
||||
},
|
||||
});
|
||||
|
|
@ -129,7 +121,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
let list;
|
||||
patchWithCleanup(listView.Controller.prototype, {
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
super.setup(...arguments);
|
||||
list = this;
|
||||
},
|
||||
});
|
||||
|
|
@ -149,7 +141,6 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
|
||||
await click(target, ".modal-header button.btn-close");
|
||||
await nextTick();
|
||||
await legacyExtraNextTick();
|
||||
assert.containsNone(target, ".modal");
|
||||
assert.containsNone(target, ".o_list_view");
|
||||
assert.containsOnce(target, ".o_kanban_view");
|
||||
|
|
@ -163,7 +154,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
let list;
|
||||
patchWithCleanup(listView.Controller.prototype, {
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
super.setup(...arguments);
|
||||
list = this;
|
||||
},
|
||||
});
|
||||
|
|
@ -181,30 +172,18 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
list.env.config.historyBack();
|
||||
assert.verifySteps(["on_close"], "should have called the on_close handler");
|
||||
await nextTick();
|
||||
await legacyExtraNextTick();
|
||||
assert.containsOnce(target, ".o_list_view");
|
||||
assert.containsNone(target, ".modal");
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("web client is not deadlocked when a view crashes", async function (assert) {
|
||||
assert.expect(6);
|
||||
const handler = (ev) => {
|
||||
assert.step("error");
|
||||
// need to preventDefault to remove error from console (so python test pass)
|
||||
ev.preventDefault();
|
||||
};
|
||||
// fake error service so that the odoo qunit handlers don't think that they need to handle the error
|
||||
registry.category("services").add("error", { start: () => {} });
|
||||
window.addEventListener("unhandledrejection", handler);
|
||||
registerCleanup(() => window.removeEventListener("unhandledrejection", handler));
|
||||
patchWithCleanup(QUnit, {
|
||||
onUnhandledRejection: () => {},
|
||||
});
|
||||
assert.expect(4);
|
||||
assert.expectErrors();
|
||||
|
||||
const readOnFirstRecordDef = testUtils.makeTestPromise();
|
||||
const mockRPC = (route, args) => {
|
||||
if (args.method === "read" && args.args[0][0] === 1) {
|
||||
const mockRPC = (route, { method, args, kwargs }) => {
|
||||
if (method === "web_read" && args[0][0] === 1) {
|
||||
return readOnFirstRecordDef;
|
||||
}
|
||||
};
|
||||
|
|
@ -213,17 +192,15 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
// open first record in form view. this will crash and will not
|
||||
// display a form view
|
||||
await testUtils.dom.click($(target).find(".o_list_view .o_data_cell:first"));
|
||||
assert.verifySteps([]);
|
||||
await legacyExtraNextTick();
|
||||
readOnFirstRecordDef.reject(new Error("not working as intended"));
|
||||
await nextTick();
|
||||
assert.verifySteps(["error"]);
|
||||
assert.verifyErrors(["not working as intended"]);
|
||||
|
||||
assert.containsOnce(target, ".o_list_view", "there should still be a list view in dom");
|
||||
// open another record, the read will not crash
|
||||
await testUtils.dom.click(
|
||||
$(target).find(".o_list_view .o_data_row:eq(2) .o_data_cell:first")
|
||||
);
|
||||
await legacyExtraNextTick();
|
||||
assert.containsNone(target, ".o_list_view", "there should not be a list view in dom");
|
||||
assert.containsOnce(target, ".o_form_view", "there should be a form view in dom");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,14 +3,15 @@
|
|||
import {
|
||||
click,
|
||||
getFixture,
|
||||
legacyExtraNextTick,
|
||||
getNodesTextContent,
|
||||
makeDeferred,
|
||||
nextTick,
|
||||
} from "@web/../tests/helpers/utils";
|
||||
import { ControlPanel } from "@web/search/control_panel/control_panel";
|
||||
import { SearchBar } from "@web/search/search_bar/search_bar";
|
||||
import {
|
||||
isItemSelected,
|
||||
toggleFilterMenu,
|
||||
toggleSearchBarMenu,
|
||||
toggleMenuItem,
|
||||
switchView,
|
||||
} from "@web/../tests/search/helpers";
|
||||
|
|
@ -23,9 +24,13 @@ import {
|
|||
loadState,
|
||||
} from "@web/../tests/webclient/helpers";
|
||||
|
||||
import { Component, xml } from "@odoo/owl";
|
||||
import { Component, onWillStart, xml } from "@odoo/owl";
|
||||
const actionRegistry = registry.category("actions");
|
||||
|
||||
function getBreadCrumbTexts(target) {
|
||||
return getNodesTextContent(target.querySelectorAll(".breadcrumb-item, .o_breadcrumb .active"));
|
||||
}
|
||||
|
||||
let serverData;
|
||||
let target;
|
||||
|
||||
|
|
@ -109,19 +114,14 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
doAction(webClient, 4);
|
||||
def.resolve();
|
||||
await nextTick();
|
||||
await legacyExtraNextTick();
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_control_panel .breadcrumb-item.active").text(),
|
||||
"Partners Action 4",
|
||||
"action 4 should be loaded"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners Action 4"]);
|
||||
});
|
||||
|
||||
QUnit.test("clicking quickly on breadcrumbs...", async function (assert) {
|
||||
assert.expect(1);
|
||||
let def;
|
||||
const mockRPC = async function (route, args) {
|
||||
if (args && args.method === "read") {
|
||||
if (args && args.method === "web_read") {
|
||||
await def;
|
||||
}
|
||||
};
|
||||
|
|
@ -140,11 +140,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
// resolve the form view read
|
||||
def.resolve();
|
||||
await nextTick();
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_control_panel .breadcrumb-item.active").text(),
|
||||
"Partners Action 4",
|
||||
"action 4 should be loaded and visible"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners Action 4"]);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
|
|
@ -181,7 +177,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
"/web/webclient/load_menus",
|
||||
"/web/action/load",
|
||||
"get_views",
|
||||
"read",
|
||||
"web_read",
|
||||
"web_search_read",
|
||||
"/web/action/load",
|
||||
"get_views",
|
||||
|
|
@ -231,7 +227,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
"/web/action/load",
|
||||
"get_views",
|
||||
"web_search_read",
|
||||
"read",
|
||||
"web_read",
|
||||
"object",
|
||||
"/web/action/load",
|
||||
"get_views",
|
||||
|
|
@ -264,7 +260,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
let def;
|
||||
const mockRPC = async function (route, args) {
|
||||
assert.step((args && args.method) || route);
|
||||
if (args && args.method === "read") {
|
||||
if (args && args.method === "web_read") {
|
||||
await def;
|
||||
}
|
||||
};
|
||||
|
|
@ -292,7 +288,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
"/web/action/load",
|
||||
"get_views",
|
||||
"web_search_read",
|
||||
"read",
|
||||
"web_read",
|
||||
"/web/action/load",
|
||||
"get_views",
|
||||
"web_search_read",
|
||||
|
|
@ -335,11 +331,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
await nextTick();
|
||||
assert.containsOnce(target, ".o_kanban_view", "should display the kanban view of action 4");
|
||||
assert.containsNone(target, ".o_list_view", "should not display the list view of action 3");
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".o_control_panel .breadcrumb-item",
|
||||
"there should be one controller in the breadcrumbs"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners Action 4"]);
|
||||
assert.verifySteps([
|
||||
"/web/webclient/load_menus",
|
||||
"/web/action/load",
|
||||
|
|
@ -370,11 +362,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
await nextTick();
|
||||
assert.containsOnce(target, ".o_kanban_view", "should display the kanban view of action 4");
|
||||
assert.containsNone(target, ".o_list_view", "should not display the list view of action 3");
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".o_control_panel .breadcrumb-item",
|
||||
"there should be one controller in the breadcrumbs"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners Action 4"]);
|
||||
assert.verifySteps([
|
||||
"/web/webclient/load_menus",
|
||||
"/web/action/load",
|
||||
|
|
@ -389,8 +377,8 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
QUnit.test("open a record while reloading the list view", async function (assert) {
|
||||
assert.expect(10);
|
||||
let def;
|
||||
const mockRPC = async function (route) {
|
||||
if (route === "/web/dataset/search_read") {
|
||||
const mockRPC = async function (route, args) {
|
||||
if (args.method === "web_search_read") {
|
||||
await def;
|
||||
}
|
||||
};
|
||||
|
|
@ -398,12 +386,12 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
await doAction(webClient, 3);
|
||||
assert.containsOnce(target, ".o_list_view");
|
||||
assert.containsN(target, ".o_list_view .o_data_row", 5);
|
||||
assert.containsOnce(target, ".o_control_panel .o_list_buttons");
|
||||
assert.containsOnce(target, ".o_control_panel .d-none.d-xl-inline-flex .o_list_buttons");
|
||||
// reload (the search_read RPC will be blocked)
|
||||
def = makeDeferred();
|
||||
await switchView(target, "list");
|
||||
assert.containsN(target, ".o_list_view .o_data_row", 5);
|
||||
assert.containsOnce(target, ".o_control_panel .o_list_buttons");
|
||||
assert.containsOnce(target, ".o_control_panel .d-none.d-xl-inline-flex .o_list_buttons");
|
||||
// open a record in form view
|
||||
await click(target.querySelector(".o_list_view .o_data_cell"));
|
||||
assert.containsOnce(target, ".o_form_view");
|
||||
|
|
@ -423,7 +411,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
const slowWillStartDef = makeDeferred();
|
||||
class ClientAction extends Component {
|
||||
setup() {
|
||||
owl.onWillStart(() => slowWillStartDef);
|
||||
onWillStart(() => slowWillStartDef);
|
||||
}
|
||||
}
|
||||
ClientAction.template = xml`<div class="client_action">ClientAction</div>`;
|
||||
|
|
@ -431,15 +419,12 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
const webClient = await createWebClient({ serverData });
|
||||
doAction(webClient, "slowAction");
|
||||
await nextTick();
|
||||
await legacyExtraNextTick();
|
||||
assert.containsNone(target, ".client_action", "client action isn't ready yet");
|
||||
doAction(webClient, 4);
|
||||
await nextTick();
|
||||
await legacyExtraNextTick();
|
||||
assert.containsOnce(target, ".o_kanban_view", "should have loaded a kanban view");
|
||||
slowWillStartDef.resolve();
|
||||
await nextTick();
|
||||
await legacyExtraNextTick();
|
||||
assert.containsOnce(target, ".o_kanban_view", "should still display the kanban view");
|
||||
}
|
||||
);
|
||||
|
|
@ -468,17 +453,14 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
def.resolve();
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".o_list_view");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_control_panel .breadcrumb-item").textContent,
|
||||
"Partners"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners"]);
|
||||
assert.containsNone(target, ".o_form_view");
|
||||
assert.verifySteps([
|
||||
"/web/webclient/load_menus",
|
||||
"/web/action/load",
|
||||
"get_views",
|
||||
"web_search_read",
|
||||
"read",
|
||||
"web_read",
|
||||
"/web/action/load",
|
||||
"web_search_read",
|
||||
]);
|
||||
|
|
@ -505,10 +487,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
def.resolve();
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".o_kanban_view");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_control_panel .breadcrumb-item").textContent,
|
||||
"Partners"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners"]);
|
||||
assert.containsNone(target, ".o_list_view");
|
||||
assert.verifySteps([
|
||||
"/web/webclient/load_menus",
|
||||
|
|
@ -533,17 +512,14 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
await doAction(webClient, 3);
|
||||
assert.containsOnce(target, ".o_list_view");
|
||||
def = makeDeferred();
|
||||
doAction(webClient, 4, { clearBreadcrumbs: true });
|
||||
doAction(webClient, 4);
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".o_list_view", "should still contain the list view");
|
||||
await switchView(target, "kanban");
|
||||
def.resolve();
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".o_kanban_view");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_control_panel .breadcrumb-item").textContent,
|
||||
"Partners"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners"]);
|
||||
assert.containsNone(target, ".o_list_view");
|
||||
assert.verifySteps([
|
||||
"/web/webclient/load_menus",
|
||||
|
|
@ -569,16 +545,13 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
await doAction(webClient, 3);
|
||||
assert.containsOnce(target, ".o_list_view");
|
||||
doAction(webClient, 4, { clearBreadcrumbs: true });
|
||||
doAction(webClient, 4);
|
||||
await nextTick();
|
||||
await switchView(target, "kanban");
|
||||
def.resolve();
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".o_kanban_view");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_control_panel .breadcrumb-item").textContent,
|
||||
"Partners"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners"]);
|
||||
assert.containsNone(target, ".o_list_view");
|
||||
assert.verifySteps([
|
||||
"/web/webclient/load_menus",
|
||||
|
|
@ -596,7 +569,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
const def = makeDeferred();
|
||||
const defs = [null, def];
|
||||
const mockRPC = async (route, args) => {
|
||||
if (args.method === "read") {
|
||||
if (args.method === "web_read") {
|
||||
await Promise.resolve(defs.shift());
|
||||
}
|
||||
};
|
||||
|
|
@ -616,18 +589,12 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
await click(row1.querySelector(".o_data_cell"));
|
||||
await click(row2.querySelector(".o_data_cell"));
|
||||
assert.containsOnce(target, ".o_form_view");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".breadcrumb-item.active").innerText,
|
||||
"Second record"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners", "Second record"]);
|
||||
|
||||
def.resolve();
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".o_form_view");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".breadcrumb-item.active").innerText,
|
||||
"Second record"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners", "Second record"]);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
|
|
@ -679,21 +646,21 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
return { fromId: this.id };
|
||||
},
|
||||
});
|
||||
owl.onWillStart(() => def);
|
||||
onWillStart(() => def);
|
||||
}
|
||||
}
|
||||
ToyController.template = xml`
|
||||
<div class="o_toy_view">
|
||||
<ControlPanel />
|
||||
<SearchBar />
|
||||
</div>`;
|
||||
ToyController.components = { ControlPanel };
|
||||
ToyController.components = { ControlPanel, SearchBar };
|
||||
|
||||
registry.category("views").add("toy", {
|
||||
type: "toy",
|
||||
display_name: "Toy",
|
||||
icon: "fab fa-android",
|
||||
multiRecord: true,
|
||||
searchMenuTypes: ["filter"],
|
||||
Controller: ToyController,
|
||||
});
|
||||
|
||||
|
|
@ -709,7 +676,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
],
|
||||
});
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await toggleMenuItem(target, "Foo");
|
||||
assert.ok(isItemSelected(target, "Foo"));
|
||||
|
||||
|
|
@ -721,7 +688,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
def.resolve();
|
||||
await nextTick();
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
assert.ok(isItemSelected(target, "Foo"));
|
||||
// this test is not able to detect that getGlobalState is put on the right place:
|
||||
// currentController.action.globalState contains in any case the search state
|
||||
|
|
|
|||
|
|
@ -1,15 +1,9 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import testUtils from "web.test_utils";
|
||||
import testUtils from "@web/../tests/legacy/helpers/test_utils";
|
||||
import { clearRegistryWithCleanup } from "../../helpers/mock_env";
|
||||
import {
|
||||
click,
|
||||
getFixture,
|
||||
legacyExtraNextTick,
|
||||
nextTick,
|
||||
patchWithCleanup,
|
||||
} from "../../helpers/utils";
|
||||
import { click, getFixture, nextTick, patchWithCleanup } from "../../helpers/utils";
|
||||
import { createWebClient, doAction, getActionManagerServerData } from "./../helpers";
|
||||
import { session } from "@web/session";
|
||||
|
||||
|
|
@ -37,16 +31,13 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
assert.containsNone(target, ".o_reward");
|
||||
webClient.env.services.effect.add({ type: "rainbow_man", message: "", fadeout: "no" });
|
||||
await nextTick();
|
||||
await legacyExtraNextTick();
|
||||
assert.containsOnce(target, ".o_reward");
|
||||
assert.containsOnce(target, ".o_kanban_view");
|
||||
await testUtils.dom.click(target.querySelector(".o_kanban_record"));
|
||||
await legacyExtraNextTick();
|
||||
assert.containsNone(target, ".o_reward");
|
||||
assert.containsOnce(target, ".o_kanban_view");
|
||||
webClient.env.services.effect.add({ type: "rainbow_man", message: "", fadeout: "no" });
|
||||
await nextTick();
|
||||
await legacyExtraNextTick();
|
||||
assert.containsOnce(target, ".o_reward");
|
||||
assert.containsOnce(target, ".o_kanban_view");
|
||||
// Do not force rainbow man to destroy on doAction
|
||||
|
|
@ -100,7 +91,6 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
await doAction(webClient, 6);
|
||||
await click(target.querySelector('button[name="object"]'));
|
||||
await legacyExtraNextTick();
|
||||
assert.containsOnce(target, ".o_reward");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_reward .o_reward_msg_content").textContent,
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { createWebClient, doAction, getActionManagerServerData } from "./../helpers";
|
||||
import { registerCleanup } from "../../helpers/cleanup";
|
||||
import { click, getFixture, nextTick, patchWithCleanup } from "../../helpers/utils";
|
||||
import { click, getFixture, nextTick } from "../../helpers/utils";
|
||||
import { errorService } from "@web/core/errors/error_service";
|
||||
import { ConnectionLostError } from "@web/core/network/rpc_service";
|
||||
|
||||
import { Component, xml } from "@odoo/owl";
|
||||
|
||||
|
|
@ -21,42 +21,52 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
QUnit.module("Error handling");
|
||||
|
||||
QUnit.test("error in a client action (at rendering)", async function (assert) {
|
||||
assert.expect(4);
|
||||
assert.expect(11);
|
||||
class Boom extends Component {}
|
||||
Boom.template = xml`<div><t t-esc="a.b.c"/></div>`;
|
||||
actionRegistry.add("Boom", Boom);
|
||||
|
||||
const webClient = await createWebClient({ serverData });
|
||||
assert.strictEqual(target.querySelector(".o_action_manager").innerHTML, "");
|
||||
const mockRPC = (route, args) => {
|
||||
if (args.method === "web_search_read") {
|
||||
assert.step("web_search_read");
|
||||
}
|
||||
};
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
await doAction(webClient, "1");
|
||||
const contents = target.querySelector(".o_action_manager").innerHTML;
|
||||
assert.ok(contents !== "");
|
||||
assert.containsOnce(target, ".o_kanban_view");
|
||||
assert.strictEqual(target.querySelector(".o_breadcrumb").textContent, "Partners Action 1");
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll(".o_kanban_record span")].map((el) => el.textContent),
|
||||
["yop", "blip", "gnap", "plop", "zoup"]
|
||||
);
|
||||
assert.verifySteps(["web_search_read"]);
|
||||
|
||||
try {
|
||||
await doAction(webClient, "Boom");
|
||||
} catch (e) {
|
||||
assert.ok(e.cause instanceof TypeError);
|
||||
}
|
||||
assert.strictEqual(target.querySelector(".o_action_manager").innerHTML, contents);
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".o_kanban_view");
|
||||
assert.strictEqual(target.querySelector(".o_breadcrumb").textContent, "Partners Action 1");
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll(".o_kanban_record span")].map((el) => el.textContent),
|
||||
["yop", "blip", "gnap", "plop", "zoup"]
|
||||
);
|
||||
assert.verifySteps(["web_search_read"]);
|
||||
});
|
||||
|
||||
QUnit.test("error in a client action (after the first rendering)", async function (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: () => {},
|
||||
});
|
||||
|
||||
assert.expectErrors();
|
||||
registry.category("services").add("error", errorService);
|
||||
|
||||
class Boom extends Component {
|
||||
setup() {
|
||||
this.boom = false;
|
||||
}
|
||||
get a() {
|
||||
// a bit artificial, but makes the test firefox compliant
|
||||
throw new Error("Cannot read properties of undefined (reading 'b')");
|
||||
}
|
||||
onClick() {
|
||||
this.boom = true;
|
||||
this.render();
|
||||
|
|
@ -76,6 +86,42 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
await click(document.querySelector(".my_button"));
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".my_button");
|
||||
assert.containsOnce(target, ".o_dialog_error");
|
||||
assert.containsOnce(target, ".o_error_dialog");
|
||||
assert.verifyErrors(["Cannot read properties of undefined (reading 'b')"]);
|
||||
});
|
||||
|
||||
QUnit.test("connection lost when opening form view from kanban", async function (assert) {
|
||||
assert.expectErrors();
|
||||
registry.category("services").add("error", errorService);
|
||||
|
||||
let offline = false;
|
||||
const mockRPC = (route, { method }) => {
|
||||
assert.step(method || route);
|
||||
if (offline) {
|
||||
throw new ConnectionLostError(route);
|
||||
}
|
||||
};
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
await doAction(webClient, 3);
|
||||
assert.containsOnce(target, ".o_list_view");
|
||||
|
||||
offline = true;
|
||||
await click(target.querySelector(".o_data_cell"));
|
||||
assert.containsOnce(target, ".o_list_view");
|
||||
assert.containsOnce(target, ".o_notification");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_notification").innerText,
|
||||
"Connection lost. Trying to reconnect..."
|
||||
);
|
||||
assert.verifySteps([
|
||||
"/web/webclient/load_menus",
|
||||
"/web/action/load",
|
||||
"get_views",
|
||||
"web_search_read",
|
||||
"web_read",
|
||||
"web_search_read",
|
||||
]);
|
||||
await nextTick();
|
||||
assert.verifySteps([]); // doesn't indefinitely try to reload the list
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,776 +0,0 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import testUtils from "web.test_utils";
|
||||
import ListController from "web.ListController";
|
||||
import FormView from "web.FormView";
|
||||
import ListView from "web.ListView";
|
||||
import {
|
||||
click,
|
||||
destroy,
|
||||
getFixture,
|
||||
makeDeferred,
|
||||
legacyExtraNextTick,
|
||||
patchWithCleanup,
|
||||
triggerEvents,
|
||||
} from "../../helpers/utils";
|
||||
import KanbanView from "web.KanbanView";
|
||||
import { registerCleanup } from "../../helpers/cleanup";
|
||||
import { makeTestEnv } from "../../helpers/mock_env";
|
||||
import { createWebClient, doAction, getActionManagerServerData } from "./../helpers";
|
||||
import makeTestEnvironment from "web.test_env";
|
||||
|
||||
import { ClientActionAdapter, ViewAdapter } from "@web/legacy/action_adapters";
|
||||
import { makeLegacyCrashManagerService } from "@web/legacy/utils";
|
||||
import { useDebugCategory } from "@web/core/debug/debug_context";
|
||||
import { ErrorDialog } from "@web/core/errors/error_dialogs";
|
||||
import * as cpHelpers from "@web/../tests/search/helpers";
|
||||
|
||||
import AbstractView from "web.AbstractView";
|
||||
import ControlPanel from "web.ControlPanel";
|
||||
import core from "web.core";
|
||||
import AbstractAction from "web.AbstractAction";
|
||||
import Widget from "web.Widget";
|
||||
import SystrayMenu from "web.SystrayMenu";
|
||||
import legacyViewRegistry from "web.view_registry";
|
||||
|
||||
let serverData;
|
||||
let target;
|
||||
|
||||
QUnit.module("ActionManager", (hooks) => {
|
||||
hooks.beforeEach(() => {
|
||||
registry.category("views").remove("form"); // remove new form from registry
|
||||
registry.category("views").remove("kanban"); // remove new kanban from registry
|
||||
registry.category("views").remove("list"); // remove new list from registry
|
||||
legacyViewRegistry.add("form", FormView); // add legacy form -> will be wrapped and added to new registry
|
||||
legacyViewRegistry.add("kanban", KanbanView); // add legacy kanban -> will be wrapped and added to new registry
|
||||
legacyViewRegistry.add("list", ListView); // add legacy list -> will be wrapped and added to new registry
|
||||
|
||||
serverData = getActionManagerServerData();
|
||||
target = getFixture();
|
||||
});
|
||||
|
||||
QUnit.module("Legacy tests (to eventually drop)");
|
||||
|
||||
QUnit.test("display warning as notification", async function (assert) {
|
||||
// this test can be removed as soon as the legacy layer is dropped
|
||||
assert.expect(5);
|
||||
let list;
|
||||
patchWithCleanup(ListController.prototype, {
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
list = this;
|
||||
},
|
||||
});
|
||||
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, 3);
|
||||
assert.containsOnce(target, ".o_legacy_list_view");
|
||||
list.trigger_up("warning", {
|
||||
title: "Warning!!!",
|
||||
message: "This is a warning...",
|
||||
});
|
||||
await testUtils.nextTick();
|
||||
await legacyExtraNextTick();
|
||||
assert.containsOnce(target, ".o_legacy_list_view");
|
||||
assert.containsOnce(document.body, ".o_notification.border-warning");
|
||||
assert.strictEqual($(".o_notification_title").text(), "Warning!!!");
|
||||
assert.strictEqual($(".o_notification_content").text(), "This is a warning...");
|
||||
});
|
||||
|
||||
QUnit.test("display warning as modal", async function (assert) {
|
||||
// this test can be removed as soon as the legacy layer is dropped
|
||||
assert.expect(5);
|
||||
let list;
|
||||
patchWithCleanup(ListController.prototype, {
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
list = this;
|
||||
},
|
||||
});
|
||||
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, 3);
|
||||
assert.containsOnce(target, ".o_legacy_list_view");
|
||||
list.trigger_up("warning", {
|
||||
title: "Warning!!!",
|
||||
message: "This is a warning...",
|
||||
type: "dialog",
|
||||
});
|
||||
await testUtils.nextTick();
|
||||
await legacyExtraNextTick();
|
||||
assert.containsOnce(target, ".o_legacy_list_view");
|
||||
assert.containsOnce(document.body, ".modal");
|
||||
assert.strictEqual($(".modal-title").text(), "Warning!!!");
|
||||
assert.strictEqual($(".modal-body").text(), "This is a warning...");
|
||||
});
|
||||
|
||||
QUnit.test("display multiline warning as modal", async function (assert) {
|
||||
assert.expect(5);
|
||||
let list;
|
||||
patchWithCleanup(ListController.prototype, {
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
list = this;
|
||||
},
|
||||
});
|
||||
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, 3);
|
||||
assert.containsOnce(target, ".o_legacy_list_view");
|
||||
list.trigger_up("warning", {
|
||||
title: "Warning!!!",
|
||||
message: "This is a warning...\nabc",
|
||||
type: "dialog",
|
||||
});
|
||||
await testUtils.nextTick();
|
||||
await legacyExtraNextTick();
|
||||
assert.containsOnce(target, ".o_legacy_list_view");
|
||||
assert.containsOnce(document.body, ".modal");
|
||||
assert.strictEqual($(".modal-title").text(), "Warning!!!");
|
||||
assert.strictEqual($(".modal-body")[0].innerText, "This is a warning...\nabc");
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"legacy crash manager is still properly remapped to error service",
|
||||
async function (assert) {
|
||||
// this test can be removed as soon as the legacy layer is dropped
|
||||
assert.expect(2);
|
||||
|
||||
const legacyEnv = makeTestEnvironment();
|
||||
registry
|
||||
.category("services")
|
||||
.add("legacy_crash_manager", makeLegacyCrashManagerService(legacyEnv))
|
||||
.add("dialog", {
|
||||
start() {
|
||||
return {
|
||||
add(dialogClass, props) {
|
||||
assert.strictEqual(dialogClass, ErrorDialog);
|
||||
assert.strictEqual(props.traceback, "BOOM");
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
await makeTestEnv();
|
||||
legacyEnv.services.crash_manager.show_message("BOOM");
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("redraw a controller and open debugManager does not crash", async (assert) => {
|
||||
assert.expect(11);
|
||||
|
||||
const LegacyAction = AbstractAction.extend({
|
||||
start() {
|
||||
const ret = this._super(...arguments);
|
||||
const el = document.createElement("div");
|
||||
el.classList.add("custom-action");
|
||||
this.el.append(el);
|
||||
return ret;
|
||||
},
|
||||
});
|
||||
core.action_registry.add("customLegacy", LegacyAction);
|
||||
|
||||
patchWithCleanup(ClientActionAdapter.prototype, {
|
||||
setup() {
|
||||
useDebugCategory("custom", { widget: this });
|
||||
this._super();
|
||||
},
|
||||
});
|
||||
|
||||
registry
|
||||
.category("debug")
|
||||
.category("custom")
|
||||
.add("item1", ({ widget }) => {
|
||||
assert.step("debugItems executed");
|
||||
assert.ok(widget);
|
||||
return {};
|
||||
});
|
||||
patchWithCleanup(odoo, { debug: true });
|
||||
|
||||
const mockRPC = (route) => {
|
||||
if (route.includes("check_access_rights")) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
await doAction(webClient, "customLegacy");
|
||||
assert.containsOnce(target, ".custom-action");
|
||||
assert.verifySteps([]);
|
||||
|
||||
await click(target, ".o_debug_manager button");
|
||||
assert.verifySteps(["debugItems executed"]);
|
||||
|
||||
await doAction(webClient, 5); // action in Dialog
|
||||
await click(target, ".modal .o_form_button_cancel");
|
||||
assert.containsNone(target, ".modal");
|
||||
assert.containsOnce(target, ".custom-action");
|
||||
assert.verifySteps([]);
|
||||
|
||||
// close debug menu
|
||||
await click(target, ".o_debug_manager button");
|
||||
// open debug menu
|
||||
await click(target, ".o_debug_manager button");
|
||||
assert.verifySteps(["debugItems executed"]);
|
||||
delete core.action_registry.map.customLegacy;
|
||||
});
|
||||
|
||||
QUnit.test("willUnmount is called down the legacy layers", async (assert) => {
|
||||
assert.expect(7);
|
||||
|
||||
let mountCount = 0;
|
||||
patchWithCleanup(ControlPanel.prototype, {
|
||||
setup() {
|
||||
this._super();
|
||||
owl.onMounted(() => {
|
||||
mountCount = mountCount + 1;
|
||||
this.__uniqueId = mountCount;
|
||||
assert.step(`mounted ${this.__uniqueId}`);
|
||||
});
|
||||
owl.onWillUnmount(() => {
|
||||
assert.step(`willUnmount ${this.__uniqueId}`);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const LegacyAction = AbstractAction.extend({
|
||||
hasControlPanel: true,
|
||||
start() {
|
||||
const ret = this._super(...arguments);
|
||||
const el = document.createElement("div");
|
||||
el.classList.add("custom-action");
|
||||
this.el.append(el);
|
||||
return ret;
|
||||
},
|
||||
});
|
||||
core.action_registry.add("customLegacy", LegacyAction);
|
||||
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, 1);
|
||||
await doAction(webClient, "customLegacy");
|
||||
await click(target.querySelectorAll(".breadcrumb-item")[0]);
|
||||
await legacyExtraNextTick();
|
||||
|
||||
destroy(webClient);
|
||||
|
||||
assert.verifySteps([
|
||||
"mounted 1",
|
||||
"willUnmount 1",
|
||||
"mounted 2",
|
||||
"willUnmount 2",
|
||||
"mounted 3",
|
||||
"willUnmount 3",
|
||||
]);
|
||||
|
||||
delete core.action_registry.map.customLegacy;
|
||||
});
|
||||
|
||||
QUnit.test("Checks the availability of all views in the action", async (assert) => {
|
||||
assert.expect(2);
|
||||
patchWithCleanup(ListView.prototype, {
|
||||
init(viewInfo, params) {
|
||||
const action = params.action;
|
||||
const views = action.views.map((view) => [view.viewID, view.type]);
|
||||
assert.deepEqual(views, [
|
||||
[1, "list"],
|
||||
[2, "kanban"],
|
||||
[3, "form"],
|
||||
]);
|
||||
assert.deepEqual(action._views, [
|
||||
[1, "list"],
|
||||
[2, "kanban"],
|
||||
[3, "form"],
|
||||
[false, "search"],
|
||||
]);
|
||||
this._super(...arguments);
|
||||
},
|
||||
});
|
||||
const models = {
|
||||
partner: {
|
||||
fields: {
|
||||
display_name: { string: "Displayed name", type: "char", searchable: true },
|
||||
foo: {
|
||||
string: "Foo",
|
||||
type: "char",
|
||||
default: "My little Foo Value",
|
||||
searchable: true,
|
||||
},
|
||||
bar: { string: "Bar", type: "boolean" },
|
||||
int_field: { string: "Integer field", type: "integer", group_operator: "sum" },
|
||||
},
|
||||
records: [
|
||||
{
|
||||
id: 1,
|
||||
display_name: "first record",
|
||||
foo: "yop",
|
||||
int_field: 3,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
display_name: "second record",
|
||||
foo: "lalala",
|
||||
int_field: 5,
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
display_name: "aaa",
|
||||
foo: "abc",
|
||||
int_field: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
const views = {
|
||||
"partner,1,list": '<list><field name="foo"/></list>',
|
||||
"partner,2,kanban": "<kanban></kanban>",
|
||||
"partner,3,form": `<form></form>`,
|
||||
"partner,false,search": "<search></search>",
|
||||
};
|
||||
const serverData = { models, views };
|
||||
|
||||
const webClient = await createWebClient({ serverData });
|
||||
|
||||
await doAction(webClient, {
|
||||
id: 1,
|
||||
res_model: "partner",
|
||||
type: "ir.actions.act_window",
|
||||
views: [
|
||||
[1, "list"],
|
||||
[2, "kanban"],
|
||||
[3, "form"],
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test("client actions may take and push their params", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const ClientAction = AbstractAction.extend({
|
||||
init(parent, action) {
|
||||
this._super(...arguments);
|
||||
assert.deepEqual(action.params, {
|
||||
active_id: 99,
|
||||
take: "five",
|
||||
active_ids: "1,2",
|
||||
list: [9, 10],
|
||||
});
|
||||
},
|
||||
});
|
||||
core.action_registry.add("clientAction", ClientAction);
|
||||
registerCleanup(() => delete core.action_registry.map.clientAction);
|
||||
const webClient = await createWebClient({});
|
||||
|
||||
await doAction(webClient, {
|
||||
type: "ir.actions.client",
|
||||
tag: "clientAction",
|
||||
params: {
|
||||
active_id: 99,
|
||||
take: "five",
|
||||
active_ids: "1,2",
|
||||
list: [9, 10],
|
||||
},
|
||||
});
|
||||
|
||||
assert.deepEqual(webClient.env.services.router.current.hash, {
|
||||
action: "clientAction",
|
||||
active_id: 99,
|
||||
take: "five",
|
||||
active_ids: "1,2",
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test("client actions honour do_push_state", async function (assert) {
|
||||
assert.expect(2);
|
||||
|
||||
const ClientAction = AbstractAction.extend({
|
||||
init(parent) {
|
||||
this._super(...arguments);
|
||||
this.parent = parent;
|
||||
this.parent.do_push_state({ pinball: "wizard" });
|
||||
},
|
||||
|
||||
async start() {
|
||||
await this._super(...arguments);
|
||||
const btn = document.createElement("button");
|
||||
btn.classList.add("tommy");
|
||||
btn.addEventListener("click", () => {
|
||||
this.parent.do_push_state({ gipsy: "the acid queen" });
|
||||
});
|
||||
this.el.append(btn);
|
||||
},
|
||||
|
||||
getState() {
|
||||
return {
|
||||
doctor: "quackson",
|
||||
};
|
||||
},
|
||||
});
|
||||
core.action_registry.add("clientAction", ClientAction);
|
||||
registerCleanup(() => delete core.action_registry.map.clientAction);
|
||||
const webClient = await createWebClient({});
|
||||
|
||||
await doAction(webClient, {
|
||||
type: "ir.actions.client",
|
||||
tag: "clientAction",
|
||||
});
|
||||
|
||||
assert.deepEqual(webClient.env.services.router.current.hash, {
|
||||
action: "clientAction",
|
||||
pinball: "wizard",
|
||||
doctor: "quackson",
|
||||
});
|
||||
|
||||
await click(target, ".tommy");
|
||||
assert.deepEqual(webClient.env.services.router.current.hash, {
|
||||
action: "clientAction",
|
||||
pinball: "wizard",
|
||||
gipsy: "the acid queen",
|
||||
doctor: "quackson",
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test("Systray item triggers do action on legacy service provider", async (assert) => {
|
||||
assert.expect(3);
|
||||
function createMockActionService(assert) {
|
||||
return {
|
||||
dependencies: [],
|
||||
start() {
|
||||
return {
|
||||
doAction(params) {
|
||||
assert.step("do action");
|
||||
assert.strictEqual(params, 128, "The doAction parameters are invalid.");
|
||||
},
|
||||
loadState() {},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
registry.category("services").add("action", createMockActionService(assert));
|
||||
const FakeSystrayItemWidget = Widget.extend({
|
||||
on_attach_callback() {
|
||||
this.do_action(128);
|
||||
},
|
||||
});
|
||||
SystrayMenu.Items.push(FakeSystrayItemWidget);
|
||||
await createWebClient({ serverData });
|
||||
assert.verifySteps(["do action"]);
|
||||
delete SystrayMenu.Items.FakeSystrayItemWidget;
|
||||
});
|
||||
|
||||
QUnit.test("usercontext always added to legacy actions", async (assert) => {
|
||||
assert.expect(8);
|
||||
core.action_registry.add("testClientAction", AbstractAction);
|
||||
registerCleanup(() => delete core.action_registry.map.testClientAction);
|
||||
patchWithCleanup(ClientActionAdapter.prototype, {
|
||||
setup() {
|
||||
assert.step("ClientActionAdapter");
|
||||
const action = { ...this.props.widgetArgs[0] };
|
||||
const originalAction = JSON.parse(action._originalAction);
|
||||
assert.deepEqual(originalAction.context, undefined);
|
||||
assert.deepEqual(action.context, this.env.services.user.context);
|
||||
this._super();
|
||||
},
|
||||
});
|
||||
patchWithCleanup(ViewAdapter.prototype, {
|
||||
setup() {
|
||||
assert.step("ViewAdapter");
|
||||
const action = { ...this.props.viewParams.action };
|
||||
const originalAction = JSON.parse(action._originalAction);
|
||||
assert.deepEqual(originalAction.context, undefined);
|
||||
assert.deepEqual(action.context, this.env.services.user.context);
|
||||
this._super();
|
||||
},
|
||||
});
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, "testClientAction");
|
||||
assert.verifySteps(["ClientActionAdapter"]);
|
||||
await doAction(webClient, 1);
|
||||
assert.verifySteps(["ViewAdapter"]);
|
||||
});
|
||||
|
||||
QUnit.test("correctly transports legacy Props for doAction", async (assert) => {
|
||||
assert.expect(4);
|
||||
|
||||
let ID = 0;
|
||||
const MyAction = AbstractAction.extend({
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this.ID = ID++;
|
||||
assert.step(`id: ${this.ID} props: ${JSON.stringify(arguments[2])}`);
|
||||
},
|
||||
async start() {
|
||||
const res = await this._super(...arguments);
|
||||
const link = document.createElement("a");
|
||||
link.innerText = "some link";
|
||||
link.setAttribute("id", `client_${this.ID}`);
|
||||
link.addEventListener("click", () => {
|
||||
this.do_action("testClientAction", {
|
||||
clear_breadcrumbs: true,
|
||||
props: { chain: "never break" },
|
||||
});
|
||||
});
|
||||
|
||||
this.el.appendChild(link);
|
||||
return res;
|
||||
},
|
||||
});
|
||||
core.action_registry.add("testClientAction", MyAction);
|
||||
registerCleanup(() => delete core.action_registry.map.testClientAction);
|
||||
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, "testClientAction");
|
||||
assert.verifySteps(['id: 0 props: {"className":"o_action","breadcrumbs":[]}']);
|
||||
|
||||
await click(document.getElementById("client_0"));
|
||||
assert.verifySteps([
|
||||
'id: 1 props: {"chain":"never break","className":"o_action","breadcrumbs":[]}',
|
||||
]);
|
||||
});
|
||||
|
||||
QUnit.test("bootstrap tooltip in dialog action auto destroy", async (assert) => {
|
||||
assert.expect(2);
|
||||
|
||||
const mockRPC = (route) => {
|
||||
if (route === "/web/dataset/call_button") {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
serverData.views["partner,3,form"] = /*xml*/ `
|
||||
<form>
|
||||
<field name="display_name" />
|
||||
<footer>
|
||||
<button name="echoes" type="object" string="Echoes" help="echoes"/>
|
||||
</footer>
|
||||
</form>
|
||||
`;
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
|
||||
await doAction(webClient, 25);
|
||||
|
||||
const tooltipProm = makeDeferred();
|
||||
$(target).one("shown.bs.tooltip", () => {
|
||||
tooltipProm.resolve();
|
||||
});
|
||||
|
||||
triggerEvents(target, ".modal footer button", ["mouseover", "focusin"]);
|
||||
await tooltipProm;
|
||||
// check on webClient dom
|
||||
assert.containsOnce(document.body, ".tooltip");
|
||||
await doAction(webClient, {
|
||||
type: "ir.actions.act_window_close",
|
||||
});
|
||||
// check on the whole DOM
|
||||
assert.containsNone(document.body, ".tooltip");
|
||||
});
|
||||
|
||||
QUnit.test("bootstrap tooltip destroyed on click", async (assert) => {
|
||||
assert.expect(2);
|
||||
|
||||
const mockRPC = (route) => {
|
||||
if (route === "/web/dataset/call_button") {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
serverData.views["partner,666,form"] = /*xml*/ `
|
||||
<form>
|
||||
<header>
|
||||
<button name="echoes" type="object" string="Echoes" help="echoes"/>
|
||||
</header>
|
||||
<field name="display_name" />
|
||||
</form>
|
||||
`;
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
|
||||
await doAction(webClient, 24);
|
||||
|
||||
const tooltipProm = makeDeferred();
|
||||
$(target).one("shown.bs.tooltip", () => {
|
||||
tooltipProm.resolve();
|
||||
});
|
||||
|
||||
triggerEvents(target, ".o_form_statusbar button", ["mouseover", "focusin"]);
|
||||
await tooltipProm;
|
||||
// check on webClient DOM
|
||||
assert.containsOnce(document.body, ".tooltip");
|
||||
await click(target, ".o_content");
|
||||
// check on the whole DOM
|
||||
assert.containsNone(document.body, ".tooltip");
|
||||
});
|
||||
|
||||
QUnit.test("breadcrumbs are correct in stacked legacy client actions", async function (assert) {
|
||||
const ClientAction = AbstractAction.extend({
|
||||
hasControlPanel: true,
|
||||
async start() {
|
||||
this.$el.addClass("client_action");
|
||||
return this._super(...arguments);
|
||||
},
|
||||
getTitle() {
|
||||
return "Blabla";
|
||||
},
|
||||
});
|
||||
core.action_registry.add("clientAction", ClientAction);
|
||||
registerCleanup(() => delete core.action_registry.map.clientAction);
|
||||
|
||||
const webClient = await createWebClient({ serverData });
|
||||
|
||||
await doAction(webClient, 3);
|
||||
assert.containsOnce(target, ".o_legacy_list_view");
|
||||
assert.strictEqual($(target).find(".breadcrumb-item").text(), "Partners");
|
||||
|
||||
await doAction(webClient, {
|
||||
type: "ir.actions.client",
|
||||
tag: "clientAction",
|
||||
});
|
||||
assert.containsOnce(target, ".client_action");
|
||||
assert.strictEqual($(target).find(".breadcrumb-item").text(), "PartnersBlabla");
|
||||
});
|
||||
|
||||
QUnit.test("view with js_class attribute (legacy)", async function (assert) {
|
||||
assert.expect(2);
|
||||
const TestView = AbstractView.extend({
|
||||
viewType: "test_view",
|
||||
});
|
||||
const TestJsClassView = TestView.extend({
|
||||
init() {
|
||||
this._super.call(this, ...arguments);
|
||||
assert.step("init js class");
|
||||
},
|
||||
});
|
||||
serverData.views["partner,false,test_view"] = `<div js_class="test_jsClass"></div>`;
|
||||
serverData.actions[9999] = {
|
||||
id: 1,
|
||||
name: "Partners Action 1",
|
||||
res_model: "partner",
|
||||
type: "ir.actions.act_window",
|
||||
views: [[false, "test_view"]],
|
||||
};
|
||||
legacyViewRegistry.add("test_view", TestView);
|
||||
legacyViewRegistry.add("test_jsClass", TestJsClassView);
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, 9999);
|
||||
assert.verifySteps(["init js class"]);
|
||||
delete legacyViewRegistry.map.test_view;
|
||||
delete legacyViewRegistry.map.test_jsClass;
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"execute action without modal closes bootstrap tooltips anyway",
|
||||
async function (assert) {
|
||||
assert.expect(12);
|
||||
Object.assign(serverData.views, {
|
||||
"partner,666,form": `<form>
|
||||
<header>
|
||||
<button name="object" string="Call method" type="object" help="need somebody"/>
|
||||
</header>
|
||||
<field name="display_name"/>
|
||||
</form>`,
|
||||
});
|
||||
const mockRPC = async (route) => {
|
||||
assert.step(route);
|
||||
if (route === "/web/dataset/call_button") {
|
||||
// Some business stuff server side, then return an implicit close action
|
||||
return Promise.resolve(false);
|
||||
}
|
||||
};
|
||||
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
await doAction(webClient, 24);
|
||||
assert.verifySteps([
|
||||
"/web/webclient/load_menus",
|
||||
"/web/action/load",
|
||||
"/web/dataset/call_kw/partner/get_views",
|
||||
"/web/dataset/call_kw/partner/read",
|
||||
]);
|
||||
assert.containsN(target, ".o_form_buttons_view button:not([disabled])", 2);
|
||||
const actionButton = target.querySelector("button[name=object]");
|
||||
const tooltipProm = new Promise((resolve) => {
|
||||
document.body.addEventListener(
|
||||
"shown.bs.tooltip",
|
||||
() => {
|
||||
actionButton.dispatchEvent(new Event("mouseout"));
|
||||
resolve();
|
||||
},
|
||||
{
|
||||
once: true,
|
||||
}
|
||||
);
|
||||
});
|
||||
actionButton.dispatchEvent(new Event("mouseover"));
|
||||
await tooltipProm;
|
||||
assert.containsOnce(document.body, ".tooltip");
|
||||
await click(actionButton);
|
||||
await legacyExtraNextTick();
|
||||
assert.verifySteps(["/web/dataset/call_button", "/web/dataset/call_kw/partner/read"]);
|
||||
assert.containsNone(document.body, ".tooltip"); // body different from webClient in tests !
|
||||
assert.containsN(target, ".o_form_buttons_view button:not([disabled])", 2);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("click multiple times to open a record", async function (assert) {
|
||||
assert.expect(5);
|
||||
|
||||
const def = testUtils.makeTestPromise();
|
||||
const defs = [null, def];
|
||||
const mockRPC = async (route, args) => {
|
||||
if (args.method === "read") {
|
||||
await Promise.resolve(defs.shift());
|
||||
}
|
||||
};
|
||||
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
await doAction(webClient, 3);
|
||||
|
||||
assert.containsOnce(target, ".o_legacy_list_view");
|
||||
|
||||
await testUtils.dom.click(target.querySelector(".o_legacy_list_view .o_data_row"));
|
||||
await legacyExtraNextTick();
|
||||
assert.containsOnce(target, ".o_legacy_form_view");
|
||||
|
||||
await testUtils.dom.click(target.querySelector(".o_back_button"));
|
||||
await legacyExtraNextTick();
|
||||
|
||||
assert.containsOnce(target, ".o_legacy_list_view");
|
||||
|
||||
await testUtils.dom.click(target.querySelector(".o_legacy_list_view .o_data_row"));
|
||||
await testUtils.dom.click(target.querySelector(".o_legacy_list_view .o_data_row"));
|
||||
await legacyExtraNextTick();
|
||||
assert.containsOnce(target, ".o_legacy_list_view");
|
||||
|
||||
def.resolve();
|
||||
await testUtils.nextTick();
|
||||
await legacyExtraNextTick();
|
||||
assert.containsOnce(target, ".o_legacy_form_view");
|
||||
});
|
||||
|
||||
QUnit.test("correct pager when coming from list (legacy)", async (assert) => {
|
||||
assert.expect(4);
|
||||
|
||||
registry.category("views").remove("list");
|
||||
legacyViewRegistry.add("list", ListView);
|
||||
serverData.views = {
|
||||
"partner,false,search": `<search />`,
|
||||
"partner,99,list": `<list limit="4"><field name="display_name" /></list>`,
|
||||
"partner,100,form": `<form><field name="display_name" /></form>`,
|
||||
};
|
||||
|
||||
const wc = await createWebClient({ serverData });
|
||||
await doAction(wc, {
|
||||
res_model: "partner",
|
||||
type: "ir.actions.act_window",
|
||||
views: [
|
||||
[99, "list"],
|
||||
[100, "form"],
|
||||
],
|
||||
});
|
||||
|
||||
assert.deepEqual(cpHelpers.getPagerValue(target), [1, 4]);
|
||||
assert.deepEqual(cpHelpers.getPagerLimit(target), 5);
|
||||
|
||||
await click(target, ".o_data_row:nth-child(2) .o_data_cell");
|
||||
await legacyExtraNextTick();
|
||||
assert.deepEqual(cpHelpers.getPagerValue(target), [2]);
|
||||
assert.deepEqual(cpHelpers.getPagerLimit(target), 4);
|
||||
});
|
||||
});
|
||||
|
|
@ -3,22 +3,23 @@
|
|||
import { browser } from "@web/core/browser/browser";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { WebClient } from "@web/webclient/webclient";
|
||||
import testUtils from "web.test_utils";
|
||||
import core from "web.core";
|
||||
import AbstractAction from "web.AbstractAction";
|
||||
import { registerCleanup } from "../../helpers/cleanup";
|
||||
import { makeTestEnv } from "../../helpers/mock_env";
|
||||
import {
|
||||
click,
|
||||
getFixture,
|
||||
legacyExtraNextTick,
|
||||
patchWithCleanup,
|
||||
mount,
|
||||
nextTick,
|
||||
makeDeferred,
|
||||
editInput,
|
||||
getNodesTextContent,
|
||||
} from "../../helpers/utils";
|
||||
import { pagerNext, toggleFilterMenu, toggleMenuItem } from "@web/../tests/search/helpers";
|
||||
import {
|
||||
pagerNext,
|
||||
switchView,
|
||||
toggleMenuItem,
|
||||
toggleSearchBarMenu,
|
||||
} from "@web/../tests/search/helpers";
|
||||
import { session } from "@web/session";
|
||||
import {
|
||||
createWebClient,
|
||||
|
|
@ -29,7 +30,11 @@ import {
|
|||
} from "./../helpers";
|
||||
import { errorService } from "@web/core/errors/error_service";
|
||||
|
||||
import { Component, xml } from "@odoo/owl";
|
||||
import { Component, onMounted, xml } from "@odoo/owl";
|
||||
|
||||
function getBreadCrumbTexts(target) {
|
||||
return getNodesTextContent(target.querySelectorAll(".breadcrumb-item, .o_breadcrumb .active"));
|
||||
}
|
||||
|
||||
let serverData;
|
||||
let target;
|
||||
|
|
@ -118,7 +123,8 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
patchWithCleanup(session, { home_action_id: 1001 });
|
||||
|
||||
await createWebClient({ serverData });
|
||||
await testUtils.nextTick(); // wait for the navbar to be updated
|
||||
await nextTick(); // wait for the navbar to be updated
|
||||
await nextTick(); // wait for the action to be displayed
|
||||
|
||||
assert.containsOnce(target, ".test_client_action");
|
||||
assert.strictEqual(target.querySelector(".o_menu_brand").innerText, "App1");
|
||||
|
|
@ -169,8 +175,8 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
|
||||
QUnit.test("should not crash on invalid state", async function (assert) {
|
||||
assert.expect(3);
|
||||
const mockRPC = async function (route, args) {
|
||||
assert.step((args && args.method) || route);
|
||||
const mockRPC = async function (route, { method }) {
|
||||
assert.step(method || route);
|
||||
};
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
await loadState(webClient, {
|
||||
|
|
@ -185,14 +191,17 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
class ClientAction extends Component {}
|
||||
ClientAction.template = xml`<div class="o_client_action_test">Hello World</div>`;
|
||||
actionRegistry.add("HelloWorldTest", ClientAction);
|
||||
const mockRPC = async function (route, args) {
|
||||
assert.step((args && args.method) || route);
|
||||
const mockRPC = async function (route, { method }) {
|
||||
assert.step(method || route);
|
||||
};
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
webClient.env.bus.trigger("test:hashchange", {
|
||||
action: "HelloWorldTest",
|
||||
});
|
||||
await testUtils.nextTick();
|
||||
await new Promise((r) => setTimeout(r)); // real "hashchange" event is triggered after a setTimeout [1]
|
||||
// [1] https://github.com/odoo/odoo/blob/1882d8f89f760bd1ff8a2bf0ae798939402647a3/addons/web/static/tests/setup.js#L52
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_client_action_test").text(),
|
||||
"Hello World",
|
||||
|
|
@ -203,15 +212,16 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
|
||||
QUnit.test("properly load act window actions", async function (assert) {
|
||||
assert.expect(7);
|
||||
const mockRPC = async function (route, args) {
|
||||
assert.step((args && args.method) || route);
|
||||
const mockRPC = async function (route, { method }) {
|
||||
assert.step(method || route);
|
||||
};
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
webClient.env.bus.trigger("test:hashchange", {
|
||||
action: 1,
|
||||
});
|
||||
await testUtils.nextTick();
|
||||
await legacyExtraNextTick();
|
||||
await new Promise((r) => setTimeout(r)); // real "hashchange" event is triggered after a setTimeout [1]
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".o_control_panel");
|
||||
assert.containsOnce(target, ".o_kanban_view");
|
||||
assert.verifySteps([
|
||||
|
|
@ -224,29 +234,26 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
|
||||
QUnit.test("properly load records", async function (assert) {
|
||||
assert.expect(6);
|
||||
const mockRPC = async function (route, args) {
|
||||
assert.step((args && args.method) || route);
|
||||
const mockRPC = async function (route, { method }) {
|
||||
assert.step(method || route);
|
||||
};
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
webClient.env.bus.trigger("test:hashchange", {
|
||||
id: 2,
|
||||
model: "partner",
|
||||
});
|
||||
await testUtils.nextTick();
|
||||
await legacyExtraNextTick();
|
||||
await new Promise((r) => setTimeout(r)); // real "hashchange" event is triggered after a setTimeout [1]
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".o_form_view");
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_control_panel .breadcrumb-item").text(),
|
||||
"Second record",
|
||||
"should have opened the second record"
|
||||
);
|
||||
assert.verifySteps(["/web/webclient/load_menus", "get_views", "read"]);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Second record"]);
|
||||
assert.verifySteps(["/web/webclient/load_menus", "get_views", "web_read"]);
|
||||
});
|
||||
|
||||
QUnit.test("properly load records with existing first APP", async function (assert) {
|
||||
assert.expect(7);
|
||||
const mockRPC = async function (route, args) {
|
||||
assert.step((args && args.method) || route);
|
||||
const mockRPC = async function (route, { method }) {
|
||||
assert.step(method || route);
|
||||
};
|
||||
// simulate a real scenario with a first app (e.g. Discuss), to ensure that we don't
|
||||
// fallback on that first app when only a model and res_id are given in the url
|
||||
|
|
@ -259,21 +266,17 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
Object.assign(browser.location, { hash });
|
||||
await createWebClient({ serverData, mockRPC });
|
||||
|
||||
await testUtils.nextTick();
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".o_form_view");
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_control_panel .breadcrumb-item").text(),
|
||||
"Second record",
|
||||
"should have opened the second record"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Second record"]);
|
||||
assert.containsNone(target, ".o_menu_brand");
|
||||
assert.verifySteps(["/web/webclient/load_menus", "get_views", "read"]);
|
||||
assert.verifySteps(["/web/webclient/load_menus", "get_views", "web_read"]);
|
||||
});
|
||||
|
||||
QUnit.test("properly load default record", async function (assert) {
|
||||
assert.expect(6);
|
||||
const mockRPC = async function (route, args) {
|
||||
assert.step((args && args.method) || route);
|
||||
const mockRPC = async function (route, { method }) {
|
||||
assert.step(method || route);
|
||||
};
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
webClient.env.bus.trigger("test:hashchange", {
|
||||
|
|
@ -282,8 +285,9 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
model: "partner",
|
||||
view_type: "form",
|
||||
});
|
||||
await testUtils.nextTick();
|
||||
await legacyExtraNextTick();
|
||||
await new Promise((r) => setTimeout(r)); // real "hashchange" event is triggered after a setTimeout [1]
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".o_form_view");
|
||||
assert.verifySteps([
|
||||
"/web/webclient/load_menus",
|
||||
|
|
@ -295,16 +299,17 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
|
||||
QUnit.test("load requested view for act window actions", async function (assert) {
|
||||
assert.expect(7);
|
||||
const mockRPC = async function (route, args) {
|
||||
assert.step((args && args.method) || route);
|
||||
const mockRPC = async function (route, { method }) {
|
||||
assert.step(method || route);
|
||||
};
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
webClient.env.bus.trigger("test:hashchange", {
|
||||
action: 3,
|
||||
view_type: "kanban",
|
||||
});
|
||||
await testUtils.nextTick();
|
||||
await legacyExtraNextTick();
|
||||
await new Promise((r) => setTimeout(r)); // real "hashchange" event is triggered after a setTimeout [1]
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
assert.containsNone(target, ".o_list_view");
|
||||
assert.containsOnce(target, ".o_kanban_view");
|
||||
assert.verifySteps([
|
||||
|
|
@ -318,9 +323,13 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
QUnit.test(
|
||||
"lazy load multi record view if mono record one is requested",
|
||||
async function (assert) {
|
||||
assert.expect(12);
|
||||
const mockRPC = async function (route, args) {
|
||||
assert.step((args && args.method) || route);
|
||||
assert.expect(11);
|
||||
const mockRPC = async function (route, { method, kwargs }) {
|
||||
if (method === "unity_read") {
|
||||
assert.step(`unity_read ${kwargs.method}`);
|
||||
} else {
|
||||
assert.step(method || route);
|
||||
}
|
||||
};
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
webClient.env.bus.trigger("test:hashchange", {
|
||||
|
|
@ -328,82 +337,49 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
id: 2,
|
||||
view_type: "form",
|
||||
});
|
||||
await testUtils.nextTick();
|
||||
await legacyExtraNextTick();
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
assert.containsNone(target, ".o_list_view");
|
||||
assert.containsOnce(target, ".o_form_view");
|
||||
assert.containsN(target, ".o_control_panel .breadcrumb-item", 2);
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_control_panel .breadcrumb-item:last").text(),
|
||||
"Second record",
|
||||
"breadcrumbs should contain the display_name of the opened record"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners", "Second record"]);
|
||||
// go back to List
|
||||
await testUtils.dom.click($(target).find(".o_control_panel .breadcrumb a"));
|
||||
await legacyExtraNextTick();
|
||||
await click(target.querySelector(".o_control_panel .breadcrumb a"));
|
||||
assert.containsOnce(target, ".o_list_view");
|
||||
assert.containsNone(target, ".o_form_view");
|
||||
assert.verifySteps([
|
||||
"/web/webclient/load_menus",
|
||||
"/web/action/load",
|
||||
"get_views",
|
||||
"read",
|
||||
"web_read",
|
||||
"web_search_read",
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("lazy load multi record view with previous action", async function (assert) {
|
||||
assert.expect(6);
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, 4);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".o_control_panel .breadcrumb li",
|
||||
"there should be one controller in the breadcrumbs"
|
||||
);
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_control_panel .breadcrumb li").text(),
|
||||
"Partners Action 4",
|
||||
"breadcrumbs should contain the display_name of the opened record"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners Action 4"]);
|
||||
await doAction(webClient, 3, {
|
||||
props: { resId: 2 },
|
||||
viewType: "form",
|
||||
});
|
||||
assert.containsN(
|
||||
target,
|
||||
".o_control_panel .breadcrumb li",
|
||||
3,
|
||||
"there should be three controllers in the breadcrumbs"
|
||||
);
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_control_panel .breadcrumb li").text(),
|
||||
"Partners Action 4PartnersSecond record",
|
||||
"the breadcrumb elements should be correctly ordered"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), [
|
||||
"Partners Action 4",
|
||||
"Partners",
|
||||
"Second record",
|
||||
]);
|
||||
// go back to List
|
||||
await testUtils.dom.click($(target).find(".o_control_panel .breadcrumb a:last"));
|
||||
await legacyExtraNextTick();
|
||||
assert.containsN(
|
||||
target,
|
||||
".o_control_panel .breadcrumb li",
|
||||
2,
|
||||
"there should be two controllers in the breadcrumbs"
|
||||
);
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_control_panel .breadcrumb li").text(),
|
||||
"Partners Action 4Partners",
|
||||
"the breadcrumb elements should be correctly ordered"
|
||||
);
|
||||
await click(target.querySelector(".o_control_panel .breadcrumb .o_back_button a"));
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners Action 4", "Partners"]);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"lazy loaded multi record view with failing mono record one",
|
||||
async function (assert) {
|
||||
assert.expect(3);
|
||||
const mockRPC = async function (route, args) {
|
||||
if (args && args.method === "read") {
|
||||
const mockRPC = async function (route, { method, kwargs }) {
|
||||
if (method === "web_read") {
|
||||
return Promise.reject();
|
||||
}
|
||||
};
|
||||
|
|
@ -421,9 +397,9 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
);
|
||||
|
||||
QUnit.test("change the viewType of the current action", async function (assert) {
|
||||
assert.expect(14);
|
||||
const mockRPC = async function (route, args) {
|
||||
assert.step((args && args.method) || route);
|
||||
assert.expect(13);
|
||||
const mockRPC = async function (route, { method }) {
|
||||
assert.step(method || route);
|
||||
};
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
await doAction(webClient, 3);
|
||||
|
|
@ -433,8 +409,9 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
action: 3,
|
||||
view_type: "kanban",
|
||||
});
|
||||
await testUtils.nextTick();
|
||||
await legacyExtraNextTick();
|
||||
await new Promise((r) => setTimeout(r)); // real "hashchange" event is triggered after a setTimeout [1]
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
assert.containsNone(target, ".o_list_view");
|
||||
assert.containsOnce(target, ".o_kanban_view");
|
||||
// switch to form view, open record 4
|
||||
|
|
@ -443,21 +420,12 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
id: 4,
|
||||
view_type: "form",
|
||||
});
|
||||
await testUtils.nextTick();
|
||||
await legacyExtraNextTick();
|
||||
await new Promise((r) => setTimeout(r)); // real "hashchange" event is triggered after a setTimeout [1]
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
assert.containsNone(target, ".o_kanban_view");
|
||||
assert.containsOnce(target, ".o_form_view");
|
||||
assert.containsN(
|
||||
target,
|
||||
".o_control_panel .breadcrumb-item",
|
||||
2,
|
||||
"there should be two controllers in the breadcrumbs"
|
||||
);
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_control_panel .breadcrumb-item:last").text(),
|
||||
"Fourth record",
|
||||
"should have opened the requested record"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners", "Fourth record"]);
|
||||
// verify steps to ensure that the whole action hasn't been re-executed
|
||||
// (if it would have been, /web/action/load and get_views would appear
|
||||
// several times)
|
||||
|
|
@ -467,46 +435,32 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
"get_views",
|
||||
"web_search_read",
|
||||
"web_search_read",
|
||||
"read",
|
||||
"web_read",
|
||||
]);
|
||||
});
|
||||
|
||||
QUnit.test("change the id of the current action", async function (assert) {
|
||||
assert.expect(12);
|
||||
const mockRPC = async function (route, args) {
|
||||
assert.step((args && args.method) || route);
|
||||
assert.expect(11);
|
||||
const mockRPC = async function (route, { method }) {
|
||||
assert.step(method || route);
|
||||
};
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
// execute action 3 and open the first record in a form view
|
||||
await doAction(webClient, 3);
|
||||
await testUtils.dom.click($(target).find(".o_list_view .o_data_cell:first"));
|
||||
await legacyExtraNextTick();
|
||||
await click(target.querySelector(".o_list_view .o_data_cell"));
|
||||
assert.containsOnce(target, ".o_form_view");
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_control_panel .breadcrumb-item:last").text(),
|
||||
"First record",
|
||||
"should have opened the first record"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners", "First record"]);
|
||||
// switch to record 4
|
||||
webClient.env.bus.trigger("test:hashchange", {
|
||||
action: 3,
|
||||
id: 4,
|
||||
view_type: "form",
|
||||
});
|
||||
await testUtils.nextTick();
|
||||
await legacyExtraNextTick();
|
||||
await new Promise((r) => setTimeout(r)); // real "hashchange" event is triggered after a setTimeout [1]
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".o_form_view");
|
||||
assert.containsN(
|
||||
target,
|
||||
".o_control_panel .breadcrumb-item",
|
||||
2,
|
||||
"there should be two controllers in the breadcrumbs"
|
||||
);
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_control_panel .breadcrumb-item:last").text(),
|
||||
"Fourth record",
|
||||
"should have switched to the requested record"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners", "Fourth record"]);
|
||||
// verify steps to ensure that the whole action hasn't been re-executed
|
||||
// (if it would have been, /web/action/load and get_views would appear
|
||||
// twice)
|
||||
|
|
@ -515,8 +469,8 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
"/web/action/load",
|
||||
"get_views",
|
||||
"web_search_read",
|
||||
"read",
|
||||
"read",
|
||||
"web_read",
|
||||
"web_read",
|
||||
]);
|
||||
});
|
||||
|
||||
|
|
@ -543,8 +497,8 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
view_type: "list",
|
||||
});
|
||||
assert.verifySteps(["push_state"], "should have pushed the final state");
|
||||
await testUtils.dom.click($(target).find("tr .o_data_cell:first"));
|
||||
await legacyExtraNextTick();
|
||||
await click(target.querySelector("tr .o_data_cell"));
|
||||
await nextTick();
|
||||
currentHash = webClient.env.services.router.current.hash;
|
||||
assert.deepEqual(currentHash, {
|
||||
action: 3,
|
||||
|
|
@ -555,164 +509,30 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
assert.verifySteps(["push_state"], "should push the state of it changes afterwards");
|
||||
});
|
||||
|
||||
QUnit.test("should not push a loaded state of a legacy client action", async function (assert) {
|
||||
assert.expect(6);
|
||||
const ClientAction = AbstractAction.extend({
|
||||
init: function (parent, action, options) {
|
||||
this._super.apply(this, arguments);
|
||||
this.controllerID = options.controllerID;
|
||||
},
|
||||
start: function () {
|
||||
const $button = $("<button id='client_action_button'>").text("Click Me!");
|
||||
$button.on("click", () => {
|
||||
this.trigger_up("push_state", {
|
||||
controllerID: this.controllerID,
|
||||
state: { someValue: "X" },
|
||||
});
|
||||
});
|
||||
this.$el.append($button);
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
const pushState = browser.history.pushState;
|
||||
patchWithCleanup(browser, {
|
||||
history: Object.assign({}, browser.history, {
|
||||
pushState() {
|
||||
pushState(...arguments);
|
||||
assert.step("push_state");
|
||||
},
|
||||
}),
|
||||
});
|
||||
core.action_registry.add("ClientAction", ClientAction);
|
||||
const webClient = await createWebClient({ serverData });
|
||||
let currentHash = webClient.env.services.router.current.hash;
|
||||
assert.deepEqual(currentHash, {});
|
||||
await loadState(webClient, { action: 9 });
|
||||
currentHash = webClient.env.services.router.current.hash;
|
||||
assert.deepEqual(currentHash, {
|
||||
action: 9,
|
||||
});
|
||||
assert.verifySteps([], "should not push the loaded state");
|
||||
await testUtils.dom.click($(target).find("#client_action_button"));
|
||||
await legacyExtraNextTick();
|
||||
assert.verifySteps(["push_state"], "should push the state of it changes afterwards");
|
||||
currentHash = webClient.env.services.router.current.hash;
|
||||
assert.deepEqual(currentHash, {
|
||||
action: 9,
|
||||
someValue: "X",
|
||||
});
|
||||
delete core.action_registry.map.ClientAction;
|
||||
});
|
||||
|
||||
QUnit.test("change a param of an ir.actions.client in the url", async function (assert) {
|
||||
assert.expect(12);
|
||||
const ClientAction = AbstractAction.extend({
|
||||
hasControlPanel: true,
|
||||
init: function (parent, action) {
|
||||
this._super.apply(this, arguments);
|
||||
const context = action.context;
|
||||
this.a = (context.params && context.params.a) || "default value";
|
||||
},
|
||||
start: function () {
|
||||
assert.step("start");
|
||||
this.$(".o_content").text(this.a);
|
||||
this.$el.addClass("o_client_action");
|
||||
this.trigger_up("push_state", {
|
||||
controllerID: this.controllerID,
|
||||
state: { a: this.a },
|
||||
});
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
const pushState = browser.history.pushState;
|
||||
patchWithCleanup(browser, {
|
||||
history: Object.assign({}, browser.history, {
|
||||
pushState() {
|
||||
pushState(...arguments);
|
||||
assert.step("push_state");
|
||||
},
|
||||
}),
|
||||
});
|
||||
core.action_registry.add("ClientAction", ClientAction);
|
||||
const webClient = await createWebClient({ serverData });
|
||||
let currentHash = webClient.env.services.router.current.hash;
|
||||
assert.deepEqual(currentHash, {});
|
||||
// execute the client action
|
||||
await doAction(webClient, 9);
|
||||
assert.verifySteps(["start", "push_state"]);
|
||||
currentHash = webClient.env.services.router.current.hash;
|
||||
assert.deepEqual(currentHash, {
|
||||
action: 9,
|
||||
a: "default value",
|
||||
});
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_client_action .o_content").text(),
|
||||
"default value",
|
||||
"should have rendered the client action"
|
||||
);
|
||||
assert.containsN(
|
||||
target,
|
||||
".o_control_panel .breadcrumb-item",
|
||||
1,
|
||||
"there should be one controller in the breadcrumbs"
|
||||
);
|
||||
// update param 'a' in the url
|
||||
await loadState(webClient, {
|
||||
action: 9,
|
||||
a: "new value",
|
||||
});
|
||||
assert.verifySteps(["start"]); // No push state since the hash hasn't changed
|
||||
currentHash = webClient.env.services.router.current.hash;
|
||||
assert.deepEqual(currentHash, {
|
||||
action: 9,
|
||||
a: "new value",
|
||||
});
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_client_action .o_content").text(),
|
||||
"new value",
|
||||
"should have rerendered the client action with the correct param"
|
||||
);
|
||||
assert.containsN(
|
||||
target,
|
||||
".o_control_panel .breadcrumb-item",
|
||||
1,
|
||||
"there should still be one controller in the breadcrumbs"
|
||||
);
|
||||
delete core.action_registry.map.ClientAction;
|
||||
});
|
||||
|
||||
QUnit.test("load a window action without id (in a multi-record view)", async function (assert) {
|
||||
assert.expect(14);
|
||||
patchWithCleanup(browser.sessionStorage, {
|
||||
getItem(k) {
|
||||
assert.step(`getItem session ${k}`);
|
||||
return this._super(k);
|
||||
return super.getItem(k);
|
||||
},
|
||||
setItem(k, v) {
|
||||
assert.step(`setItem session ${k}`);
|
||||
return this._super(k, v);
|
||||
return super.setItem(k, v);
|
||||
},
|
||||
});
|
||||
const mockRPC = async (route, args) => {
|
||||
assert.step((args && args.method) || route);
|
||||
const mockRPC = async (route, { method, kwargs }) => {
|
||||
assert.step(method || route);
|
||||
};
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
await doAction(webClient, 4);
|
||||
assert.containsOnce(target, ".o_kanban_view", "should display a kanban view");
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_control_panel .breadcrumb-item").text(),
|
||||
"Partners Action 4",
|
||||
"breadcrumbs should display the display_name of the action"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners Action 4"]);
|
||||
await loadState(webClient, {
|
||||
model: "partner",
|
||||
view_type: "list",
|
||||
});
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_control_panel .breadcrumb-item").text(),
|
||||
"Partners Action 4",
|
||||
"should still be in the same action"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners Action 4"]);
|
||||
assert.containsNone(target, ".o_kanban_view", "should no longer display a kanban view");
|
||||
assert.containsOnce(target, ".o_list_view", "should display a list view");
|
||||
assert.verifySteps([
|
||||
|
|
@ -742,11 +562,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
await loadState(webClient, { menu_id: 666 });
|
||||
assert.containsOnce(target, ".o_kanban_view", "should display a kanban view");
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_control_panel .breadcrumb-item").text(),
|
||||
"Partners Action 1",
|
||||
"breadcrumbs should display the display_name of the action"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners Action 1"]);
|
||||
assert.verifySteps([
|
||||
"/web/webclient/load_menus",
|
||||
"/web/action/load",
|
||||
|
|
@ -762,21 +578,15 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
1: { id: 1, children: [], name: "App1", appID: 1, actionID: 1 },
|
||||
};
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await legacyExtraNextTick();
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".o_kanban_view"); // action 1 (default app)
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_control_panel .breadcrumb-item").text(),
|
||||
"Partners Action 1"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners Action 1"]);
|
||||
await loadState(webClient, { action: 3 });
|
||||
assert.containsOnce(target, ".o_list_view"); // action 3
|
||||
assert.strictEqual($(target).find(".o_control_panel .breadcrumb-item").text(), "Partners");
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners"]);
|
||||
await loadState(webClient, { home: 1 });
|
||||
assert.containsOnce(target, ".o_kanban_view"); // action 1 (default app)
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_control_panel .breadcrumb-item").text(),
|
||||
"Partners Action 1"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners Action 1"]);
|
||||
});
|
||||
|
||||
QUnit.test("load state supports #home as initial state", async function (assert) {
|
||||
|
|
@ -791,12 +601,9 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
assert.step(route);
|
||||
};
|
||||
await createWebClient({ serverData, mockRPC });
|
||||
await legacyExtraNextTick();
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".o_kanban_view", "should display a kanban view");
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_control_panel .breadcrumb-item").text(),
|
||||
"Partners Action 1"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partners Action 1"]);
|
||||
assert.verifySteps([
|
||||
"/web/webclient/load_menus",
|
||||
"/web/action/load",
|
||||
|
|
@ -806,7 +613,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
});
|
||||
|
||||
QUnit.test("load state: in a form view, remove the id from the state", async function (assert) {
|
||||
assert.expect(13);
|
||||
assert.expect(11);
|
||||
serverData.actions[999] = {
|
||||
id: 999,
|
||||
name: "Partner",
|
||||
|
|
@ -823,16 +630,12 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
await doAction(webClient, 999, { viewType: "form", props: { resId: 2 } });
|
||||
assert.containsOnce(target, ".o_form_view");
|
||||
assert.containsN(target, ".breadcrumb-item", 2);
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_control_panel .breadcrumb-item.active").text(),
|
||||
"Second record"
|
||||
);
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partner", "Second record"]);
|
||||
assert.verifySteps([
|
||||
"/web/webclient/load_menus",
|
||||
"/web/action/load",
|
||||
"/web/dataset/call_kw/partner/get_views",
|
||||
"/web/dataset/call_kw/partner/read",
|
||||
"/web/dataset/call_kw/partner/web_read",
|
||||
]);
|
||||
await loadState(webClient, {
|
||||
action: 999,
|
||||
|
|
@ -841,57 +644,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
});
|
||||
assert.verifySteps(["/web/dataset/call_kw/partner/onchange"]);
|
||||
assert.containsOnce(target, ".o_form_view .o_form_editable");
|
||||
assert.containsN(target, ".breadcrumb-item", 2);
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_control_panel .breadcrumb-item.active").text(),
|
||||
"New"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("hashchange does not trigger canberemoved right away", async function (assert) {
|
||||
assert.expect(9);
|
||||
const ClientAction = AbstractAction.extend({
|
||||
start() {
|
||||
this.$el.text("Hello World");
|
||||
this.$el.addClass("o_client_action_test");
|
||||
},
|
||||
canBeRemoved() {
|
||||
assert.step("canBeRemoved");
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
const ClientAction2 = AbstractAction.extend({
|
||||
start() {
|
||||
this.$el.text("Hello World");
|
||||
this.$el.addClass("o_client_action_test_2");
|
||||
},
|
||||
canBeRemoved() {
|
||||
assert.step("canBeRemoved_2");
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
const pushState = browser.history.pushState;
|
||||
patchWithCleanup(browser, {
|
||||
history: Object.assign({}, browser.history, {
|
||||
pushState() {
|
||||
pushState(...arguments);
|
||||
assert.step("hashSet");
|
||||
},
|
||||
}),
|
||||
});
|
||||
core.action_registry.add("ClientAction", ClientAction);
|
||||
core.action_registry.add("ClientAction2", ClientAction2);
|
||||
const webClient = await createWebClient({ serverData });
|
||||
assert.verifySteps([]);
|
||||
await doAction(webClient, 9);
|
||||
assert.verifySteps(["hashSet"]);
|
||||
assert.containsOnce(target, ".o_client_action_test");
|
||||
assert.verifySteps([]);
|
||||
await doAction(webClient, "ClientAction2");
|
||||
assert.containsOnce(target, ".o_client_action_test_2");
|
||||
assert.verifySteps(["canBeRemoved", "hashSet"]);
|
||||
delete core.action_registry.map.ClientAction;
|
||||
delete core.action_registry.map.ClientAction2;
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Partner", "New"]);
|
||||
});
|
||||
|
||||
QUnit.test("state with integer active_ids should not crash", async function (assert) {
|
||||
|
|
@ -919,8 +672,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
await doAction(webClient, 3);
|
||||
assert.containsOnce(target, ".o_list_view", "should now display the list view");
|
||||
|
||||
await testUtils.controlPanel.switchView(target, "kanban");
|
||||
await legacyExtraNextTick();
|
||||
await switchView(target, "kanban");
|
||||
assert.containsOnce(target, ".o_kanban_view", "should now display the kanban view");
|
||||
|
||||
const hash = webClient.env.services.router.current.hash;
|
||||
|
|
@ -957,11 +709,10 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
});
|
||||
|
||||
await click(target.querySelector(".o_control_panel .breadcrumb-item"));
|
||||
await legacyExtraNextTick();
|
||||
|
||||
assert.containsN(target, ".o_list_view .o_data_row", 5);
|
||||
|
||||
await toggleFilterMenu(target);
|
||||
await toggleSearchBarMenu(target);
|
||||
await toggleMenuItem(target, "Filter");
|
||||
|
||||
assert.containsN(target, ".o_list_view .o_data_row", 1);
|
||||
|
|
@ -982,33 +733,23 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
},
|
||||
{ props: { resIds: [1, 2] } }
|
||||
);
|
||||
assert.strictEqual(target.querySelector(".breadcrumb").textContent, "First record");
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["First record"]);
|
||||
await pagerNext(target);
|
||||
assert.strictEqual(target.querySelector(".breadcrumb").textContent, "Second record");
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["Second record"]);
|
||||
await editInput(target, "[name=display_name] input", "new name");
|
||||
|
||||
// without saving we now make a loadState which should commit changes
|
||||
await loadState(webClient, { action: 1337, id: 1, model: "partner", view_type: "form" });
|
||||
assert.strictEqual(target.querySelector(".breadcrumb").textContent, "First record");
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["First record"]);
|
||||
|
||||
// loadState again just to check if changes were commited
|
||||
await loadState(webClient, { action: 1337, id: 2, model: "partner", view_type: "form" });
|
||||
assert.strictEqual(target.querySelector(".breadcrumb").textContent, "new name");
|
||||
assert.deepEqual(getBreadCrumbTexts(target), ["new name"]);
|
||||
});
|
||||
|
||||
QUnit.test("initial action crashes", async (assert) => {
|
||||
assert.expect(8);
|
||||
|
||||
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: () => {},
|
||||
});
|
||||
assert.expectErrors();
|
||||
|
||||
browser.location.hash = "#action=__test__client__action__&menu_id=1";
|
||||
const ClientAction = registry.category("actions").get("__test__client__action__");
|
||||
|
|
@ -1026,9 +767,10 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
const webClient = await createWebClient({ serverData });
|
||||
assert.verifySteps(["clientAction setup"]);
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".o_dialog_error");
|
||||
assert.expectErrors(["my error"]);
|
||||
assert.containsOnce(target, ".o_error_dialog");
|
||||
await click(target, ".modal-header .btn-close");
|
||||
assert.containsNone(target, ".o_dialog_error");
|
||||
assert.containsNone(target, ".o_error_dialog");
|
||||
await click(target, "nav .o_navbar_apps_menu .dropdown-toggle ");
|
||||
assert.containsN(target, ".dropdown-item.o_app", 3);
|
||||
assert.containsNone(target, ".o_menu_brand");
|
||||
|
|
@ -1043,7 +785,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
const hashchangeDef = makeDeferred();
|
||||
class MyAction extends Component {
|
||||
setup() {
|
||||
owl.onMounted(() => {
|
||||
onMounted(() => {
|
||||
assert.step("myAction mounted");
|
||||
browser.addEventListener("hashchange", () => {
|
||||
hashchangeDef.resolve();
|
||||
|
|
@ -1058,6 +800,8 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
browser.location.hash = "#action=myAction";
|
||||
|
||||
const webClient = await createWebClient({ serverData });
|
||||
assert.verifySteps([]);
|
||||
await nextTick();
|
||||
assert.verifySteps(["myAction mounted"]);
|
||||
assert.containsOnce(target, ".not-here");
|
||||
|
||||
|
|
@ -1065,6 +809,9 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
await hashchangeDef;
|
||||
await nextTick();
|
||||
assert.containsNone(target, ".not-here");
|
||||
assert.containsNone(target, ".test_client_action");
|
||||
await nextTick();
|
||||
assert.containsNone(target, ".not-here");
|
||||
assert.containsOnce(target, ".test_client_action");
|
||||
|
||||
assert.deepEqual(webClient.env.services.router.current.hash, {
|
||||
|
|
@ -1074,13 +821,13 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
});
|
||||
|
||||
QUnit.test("concurrent hashchange during action mounting -- 2", async (assert) => {
|
||||
assert.expect(5);
|
||||
assert.expect(6);
|
||||
|
||||
const baseURL = new URL(browser.location.href).toString();
|
||||
|
||||
class MyAction extends Component {
|
||||
setup() {
|
||||
owl.onMounted(() => {
|
||||
onMounted(() => {
|
||||
assert.step("myAction mounted");
|
||||
const newURL = baseURL + "#action=__test__client__action__&menu_id=1";
|
||||
// immediate triggering
|
||||
|
|
@ -1093,8 +840,11 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
|
||||
browser.location.hash = "#action=myAction";
|
||||
const webClient = await createWebClient({ serverData });
|
||||
assert.verifySteps([]);
|
||||
await nextTick();
|
||||
assert.verifySteps(["myAction mounted"]);
|
||||
|
||||
await nextTick();
|
||||
await nextTick();
|
||||
assert.containsNone(target, ".not-here");
|
||||
assert.containsOnce(target, ".test_client_action");
|
||||
|
|
@ -1115,7 +865,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
patchWithCleanup(browser.sessionStorage, {
|
||||
getItem(k) {
|
||||
assert.step(`getItem session ${k}`);
|
||||
return this._super(k);
|
||||
return super.getItem(k);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -3,34 +3,23 @@
|
|||
import { browser } from "@web/core/browser/browser";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { session } from "@web/session";
|
||||
import AbstractAction from "web.AbstractAction";
|
||||
import core from "web.core";
|
||||
import testUtils from "web.test_utils";
|
||||
import Widget from "web.Widget";
|
||||
import { makeTestEnv } from "../../helpers/mock_env";
|
||||
import {
|
||||
click,
|
||||
getFixture,
|
||||
hushConsole,
|
||||
legacyExtraNextTick,
|
||||
nextTick,
|
||||
patchWithCleanup,
|
||||
} from "../../helpers/utils";
|
||||
import testUtils from "@web/../tests/legacy/helpers/test_utils";
|
||||
import { click, getFixture, hushConsole, nextTick, patchWithCleanup } from "../../helpers/utils";
|
||||
import {
|
||||
createWebClient,
|
||||
doAction,
|
||||
getActionManagerServerData,
|
||||
setupWebClientRegistries,
|
||||
} from "./../helpers";
|
||||
import * as cpHelpers from "@web/../tests/search/helpers";
|
||||
import { listView } from "@web/views/list/list_view";
|
||||
import { companyService } from "@web/webclient/company_service";
|
||||
import { onWillStart } from "@odoo/owl";
|
||||
import { GraphModel } from "@web/views/graph/graph_model";
|
||||
import { fakeCookieService } from "../../helpers/mock_services";
|
||||
import { switchView } from "../../search/helpers";
|
||||
|
||||
let serverData;
|
||||
let target;
|
||||
// legacy stuff
|
||||
const actionRegistry = registry.category("actions");
|
||||
const actionHandlersRegistry = registry.category("action_handlers");
|
||||
|
||||
|
|
@ -108,7 +97,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
QUnit.test("properly handle case when action id does not exist", async (assert) => {
|
||||
assert.expect(2);
|
||||
const webClient = await createWebClient({ serverData });
|
||||
patchWithCleanup(window, { console: hushConsole }, { pure: true });
|
||||
patchWithCleanup(window, { console: hushConsole });
|
||||
patchWithCleanup(webClient.env.services.notification, {
|
||||
add(message) {
|
||||
assert.strictEqual(message, "No action with id '4448' could be found");
|
||||
|
|
@ -207,195 +196,30 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
assert.deepEqual(action.context, actionParams);
|
||||
});
|
||||
|
||||
QUnit.test("no widget memory leaks when doing some action stuff", async function (assert) {
|
||||
assert.expect(1);
|
||||
let delta = 0;
|
||||
testUtils.mock.patch(Widget, {
|
||||
init: function () {
|
||||
delta++;
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
destroy: function () {
|
||||
delta--;
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, 8);
|
||||
const n = delta;
|
||||
await doAction(webClient, 4);
|
||||
// kanban view is loaded, switch to list view
|
||||
await cpHelpers.switchView(target, "list");
|
||||
await legacyExtraNextTick();
|
||||
// open a record in form view
|
||||
await testUtils.dom.click(target.querySelector(".o_list_view .o_data_row"));
|
||||
await legacyExtraNextTick();
|
||||
// go back to action 7 in breadcrumbs
|
||||
await testUtils.dom.click(target.querySelector(".o_control_panel .breadcrumb a"));
|
||||
await legacyExtraNextTick();
|
||||
assert.strictEqual(delta, n, "should have properly destroyed all other widgets");
|
||||
testUtils.mock.unpatch(Widget);
|
||||
});
|
||||
|
||||
QUnit.test("no widget memory leaks when executing actions in dialog", async function (assert) {
|
||||
assert.expect(1);
|
||||
let delta = 0;
|
||||
testUtils.mock.patch(Widget, {
|
||||
init: function () {
|
||||
delta++;
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
destroy: function () {
|
||||
if (!this.isDestroyed()) {
|
||||
delta--;
|
||||
}
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
const webClient = await createWebClient({ serverData });
|
||||
const n = delta;
|
||||
await doAction(webClient, 5);
|
||||
await doAction(webClient, { type: "ir.actions.act_window_close" });
|
||||
assert.strictEqual(delta, n, "should have properly destroyed all widgets");
|
||||
testUtils.mock.unpatch(Widget);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"no memory leaks when executing an action while switching view",
|
||||
async function (assert) {
|
||||
assert.expect(1);
|
||||
let def;
|
||||
let delta = 0;
|
||||
testUtils.mock.patch(Widget, {
|
||||
init: function () {
|
||||
delta += 1;
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
destroy: function () {
|
||||
delta -= 1;
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
const mockRPC = async function (route, args) {
|
||||
if (args && args.method === "read") {
|
||||
await Promise.resolve(def);
|
||||
}
|
||||
};
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
await doAction(webClient, 4);
|
||||
const n = delta;
|
||||
await doAction(webClient, 3, { clearBreadcrumbs: true });
|
||||
// switch to the form view (this request is blocked)
|
||||
def = testUtils.makeTestPromise();
|
||||
await testUtils.dom.click(target.querySelector(".o_list_view .o_data_row"));
|
||||
// execute another action meanwhile (don't block this request)
|
||||
await doAction(webClient, 4, { clearBreadcrumbs: true });
|
||||
// unblock the switch to the form view in action 3
|
||||
def.resolve();
|
||||
await testUtils.nextTick();
|
||||
assert.strictEqual(n, delta, "all widgets of action 3 should have been destroyed");
|
||||
testUtils.mock.unpatch(Widget);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"no memory leaks when executing an action while loading views",
|
||||
async function (assert) {
|
||||
assert.expect(1);
|
||||
let def;
|
||||
let delta = 0;
|
||||
testUtils.mock.patch(Widget, {
|
||||
init: function () {
|
||||
delta += 1;
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
destroy: function () {
|
||||
delta -= 1;
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
const mockRPC = async function (route, args) {
|
||||
if (args && args.method === "get_views") {
|
||||
await Promise.resolve(def);
|
||||
}
|
||||
};
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
// execute action 4 to know the number of widgets it instantiates
|
||||
await doAction(webClient, 4);
|
||||
const n = delta;
|
||||
// execute a first action (its 'get_views' RPC is blocked)
|
||||
def = testUtils.makeTestPromise();
|
||||
doAction(webClient, 3, { clearBreadcrumbs: true });
|
||||
await testUtils.nextTick();
|
||||
await legacyExtraNextTick();
|
||||
// execute another action meanwhile (and unlock the RPC)
|
||||
doAction(webClient, 4, { clearBreadcrumbs: true });
|
||||
def.resolve();
|
||||
await testUtils.nextTick();
|
||||
await legacyExtraNextTick();
|
||||
assert.strictEqual(n, delta, "all widgets of action 3 should have been destroyed");
|
||||
testUtils.mock.unpatch(Widget);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"no memory leaks when executing an action while loading data of default view",
|
||||
async function (assert) {
|
||||
assert.expect(1);
|
||||
let def;
|
||||
let delta = 0;
|
||||
testUtils.mock.patch(Widget, {
|
||||
init: function () {
|
||||
delta += 1;
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
destroy: function () {
|
||||
delta -= 1;
|
||||
this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
const mockRPC = async function (route) {
|
||||
if (route === "/web/dataset/search_read") {
|
||||
await Promise.resolve(def);
|
||||
}
|
||||
};
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
// execute action 4 to know the number of widgets it instantiates
|
||||
await doAction(webClient, 4);
|
||||
const n = delta;
|
||||
// execute a first action (its 'search_read' RPC is blocked)
|
||||
def = testUtils.makeTestPromise();
|
||||
doAction(webClient, 3, { clearBreadcrumbs: true });
|
||||
await testUtils.nextTick();
|
||||
await legacyExtraNextTick();
|
||||
// execute another action meanwhile (and unlock the RPC)
|
||||
doAction(webClient, 4, { clearBreadcrumbs: true });
|
||||
def.resolve();
|
||||
await testUtils.nextTick();
|
||||
await legacyExtraNextTick();
|
||||
assert.strictEqual(n, delta, "all widgets of action 3 should have been destroyed");
|
||||
testUtils.mock.unpatch(Widget);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test('action with "no_breadcrumbs" set to true', async function (assert) {
|
||||
serverData.actions[4].context = { no_breadcrumbs: true };
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, 3);
|
||||
assert.containsOnce(target, ".o_control_panel .breadcrumb-item");
|
||||
assert.containsOnce(target, ".o_control_panel .o_breadcrumb");
|
||||
// push another action flagged with 'no_breadcrumbs=true'
|
||||
await doAction(webClient, 4);
|
||||
assert.containsNone(target, ".o_control_panel .breadcrumb-item");
|
||||
assert.containsOnce(target, ".o_kanban_view");
|
||||
assert.containsNone(target, ".o_control_panel .o_breadcrumb");
|
||||
await click(target, ".o_switch_view.o_list");
|
||||
assert.containsOnce(target, ".o_list_view");
|
||||
assert.containsNone(target, ".o_control_panel .o_breadcrumb");
|
||||
});
|
||||
|
||||
QUnit.test("document's title is updated when an action is executed", async function (assert) {
|
||||
const defaultTitle = { zopenerp: "Odoo" };
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await nextTick();
|
||||
let currentTitle = webClient.env.services.title.getParts();
|
||||
assert.deepEqual(currentTitle, defaultTitle);
|
||||
let currentHash = webClient.env.services.router.current.hash;
|
||||
assert.deepEqual(currentHash, {});
|
||||
await doAction(webClient, 4);
|
||||
await nextTick();
|
||||
currentTitle = webClient.env.services.title.getParts();
|
||||
assert.deepEqual(currentTitle, {
|
||||
...defaultTitle,
|
||||
|
|
@ -404,6 +228,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
currentHash = webClient.env.services.router.current.hash;
|
||||
assert.deepEqual(currentHash, { action: 4, model: "partner", view_type: "kanban" });
|
||||
await doAction(webClient, 8);
|
||||
await nextTick();
|
||||
currentTitle = webClient.env.services.title.getParts();
|
||||
assert.deepEqual(currentTitle, {
|
||||
...defaultTitle,
|
||||
|
|
@ -422,61 +247,25 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
assert.deepEqual(currentHash, { action: 8, id: 4, model: "pony", view_type: "form" });
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"on_reverse_breadcrumb handler is correctly called (legacy)",
|
||||
async function (assert) {
|
||||
// This test can be removed as soon as we no longer support legacy actions as the new
|
||||
// ActionManager doesn't support this option. Indeed, it is used to reload the previous
|
||||
// action when coming back, but we won't need such an artefact to that with Wowl, as the
|
||||
// controller will be re-instantiated with an (exported) state given in props.
|
||||
assert.expect(5);
|
||||
const ClientAction = AbstractAction.extend({
|
||||
events: {
|
||||
"click button": "_onClick",
|
||||
},
|
||||
start() {
|
||||
this.$el.html('<button class="my_button">Execute another action</button>');
|
||||
},
|
||||
_onClick() {
|
||||
this.do_action(4, {
|
||||
on_reverse_breadcrumb: () => assert.step("on_reverse_breadcrumb"),
|
||||
});
|
||||
},
|
||||
});
|
||||
core.action_registry.add("ClientAction", ClientAction);
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, "ClientAction");
|
||||
assert.containsOnce(target, ".my_button");
|
||||
await testUtils.dom.click(target.querySelector(".my_button"));
|
||||
await legacyExtraNextTick();
|
||||
assert.containsOnce(target, ".o_kanban_view");
|
||||
await testUtils.dom.click($(target).find(".o_control_panel .breadcrumb a:first"));
|
||||
await legacyExtraNextTick();
|
||||
assert.containsOnce(target, ".my_button");
|
||||
assert.verifySteps(["on_reverse_breadcrumb"]);
|
||||
delete core.action_registry.map.ClientAction;
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test('handles "history_back" event', async function (assert) {
|
||||
assert.expect(3);
|
||||
assert.expect(4);
|
||||
let list;
|
||||
patchWithCleanup(listView.Controller.prototype, {
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
super.setup(...arguments);
|
||||
list = this;
|
||||
},
|
||||
});
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, 4);
|
||||
await doAction(webClient, 3);
|
||||
assert.containsN(target, ".o_control_panel .breadcrumb-item", 2);
|
||||
assert.containsOnce(target, "ol.breadcrumb");
|
||||
assert.containsOnce(target, ".o_breadcrumb span");
|
||||
list.env.config.historyBack();
|
||||
await testUtils.nextTick();
|
||||
await legacyExtraNextTick();
|
||||
assert.containsOnce(target, ".o_control_panel .breadcrumb-item");
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".o_breadcrumb span");
|
||||
assert.strictEqual(
|
||||
$(target).find(".o_control_panel .breadcrumb-item").text(),
|
||||
target.querySelector(".o_control_panel .o_breadcrumb").textContent,
|
||||
"Partners Action 4",
|
||||
"breadcrumbs should display the display_name of the action"
|
||||
);
|
||||
|
|
@ -484,7 +273,6 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
|
||||
QUnit.test("stores and restores scroll position (in kanban)", async function (assert) {
|
||||
serverData.actions[3].views = [[false, "kanban"]];
|
||||
assert.expect(3);
|
||||
for (let i = 0; i < 60; i++) {
|
||||
serverData.models.partner.records.push({ id: 100 + i, foo: `Record ${i}` });
|
||||
}
|
||||
|
|
@ -550,6 +338,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
await click(target.querySelector(".o_form_view .o_data_row .o_data_cell"));
|
||||
assert.containsOnce(document.body, ".modal .o_form_view");
|
||||
await doAction(webClient, 1); // target != 'new'
|
||||
await nextTick(); // wait for the dialog to be closed
|
||||
assert.containsNone(document.body, ".modal");
|
||||
}
|
||||
);
|
||||
|
|
@ -588,11 +377,10 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
search_default_x: true,
|
||||
searchpanel_default_y: true,
|
||||
};
|
||||
registry.category("services").add("cookie", fakeCookieService);
|
||||
patchWithCleanup(GraphModel.prototype, {
|
||||
load(searchParams) {
|
||||
assert.deepEqual(searchParams.context, { lang: "en", tz: "taht", uid: 7 });
|
||||
return this._super.apply(this, arguments);
|
||||
return super.load(...arguments);
|
||||
},
|
||||
});
|
||||
|
||||
|
|
@ -607,7 +395,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
context,
|
||||
});
|
||||
// list view is loaded, switch to graph view
|
||||
await cpHelpers.switchView(target, "graph");
|
||||
await switchView(target, "graph");
|
||||
}
|
||||
);
|
||||
|
||||
|
|
@ -645,6 +433,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
|
||||
// Create the web client. It should execute the stored action.
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await nextTick();
|
||||
|
||||
// Check the current action context
|
||||
assert.deepEqual(webClient.env.services.action.currentController.action.context, {
|
||||
|
|
@ -657,4 +446,44 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
});
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
"action is removed while waiting for another action with selectMenu",
|
||||
async (assert) => {
|
||||
let def;
|
||||
const actionRegistry = registry.category("actions");
|
||||
const ClientAction = actionRegistry.get("__test__client__action__");
|
||||
|
||||
patchWithCleanup(ClientAction.prototype, {
|
||||
setup() {
|
||||
super.setup();
|
||||
onWillStart(() => {
|
||||
return def;
|
||||
});
|
||||
},
|
||||
});
|
||||
const webClient = await createWebClient({ serverData });
|
||||
// starting point: a kanban view
|
||||
await doAction(webClient, 4);
|
||||
const root = document.querySelector(".o_web_client");
|
||||
assert.containsOnce(root, ".o_kanban_view");
|
||||
|
||||
// select one new app in navbar menu
|
||||
def = testUtils.makeTestPromise();
|
||||
const menus = webClient.env.services.menu.getApps();
|
||||
webClient.env.services.menu.selectMenu(menus[1], { resetScreen: true });
|
||||
await nextTick();
|
||||
|
||||
// check that the action manager is empty, even though client action is loading
|
||||
assert.strictEqual(root.querySelector(".o_action_manager").textContent, "");
|
||||
|
||||
// resolve onwillstart so client action is ready
|
||||
def.resolve();
|
||||
await nextTick();
|
||||
assert.strictEqual(
|
||||
root.querySelector(".o_action_manager").textContent,
|
||||
" ClientAction_Id 1"
|
||||
);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,15 +3,8 @@
|
|||
import { browser } from "@web/core/browser/browser";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import testUtils from "web.test_utils";
|
||||
import {
|
||||
click,
|
||||
getFixture,
|
||||
legacyExtraNextTick,
|
||||
makeDeferred,
|
||||
nextTick,
|
||||
patchWithCleanup,
|
||||
} from "../../helpers/utils";
|
||||
import testUtils from "@web/../tests/legacy/helpers/test_utils";
|
||||
import { click, getFixture, makeDeferred, nextTick, patchWithCleanup } from "../../helpers/utils";
|
||||
import { createWebClient, doAction, getActionManagerServerData } from "./../helpers";
|
||||
|
||||
import { Component, xml } from "@odoo/owl";
|
||||
|
|
@ -65,6 +58,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
);
|
||||
assert.strictEqual(target.querySelector(".o_menu_brand").textContent, "App2");
|
||||
await doAction(webClient, 1001, { clearBreadcrumbs: true });
|
||||
await nextTick();
|
||||
urlState = webClient.env.services.router.current;
|
||||
assert.strictEqual(urlState.hash.action, 1001);
|
||||
assert.strictEqual(urlState.hash.menu_id, 2);
|
||||
|
|
@ -94,10 +88,12 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
let urlState = webClient.env.services.router.current;
|
||||
assert.deepEqual(urlState.hash, {});
|
||||
await doAction(webClient, "client_action_pushes");
|
||||
await nextTick();
|
||||
urlState = webClient.env.services.router.current;
|
||||
assert.strictEqual(urlState.hash.action, "client_action_pushes");
|
||||
assert.strictEqual(urlState.hash.menu_id, undefined);
|
||||
await click(target, ".test_client_action");
|
||||
await nextTick();
|
||||
urlState = webClient.env.services.router.current;
|
||||
assert.strictEqual(urlState.hash.action, "client_action_pushes");
|
||||
assert.strictEqual(urlState.hash.arbitrary, "actionPushed");
|
||||
|
|
@ -123,10 +119,12 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
assert.deepEqual(urlState.hash, {});
|
||||
await doAction(webClient, "client_action_pushes");
|
||||
await click(target, ".test_client_action");
|
||||
await nextTick();
|
||||
urlState = webClient.env.services.router.current;
|
||||
assert.strictEqual(urlState.hash.action, "client_action_pushes");
|
||||
assert.strictEqual(urlState.hash.arbitrary, "actionPushed");
|
||||
await doAction(webClient, 1001);
|
||||
await nextTick();
|
||||
urlState = webClient.env.services.router.current;
|
||||
assert.strictEqual(urlState.hash.action, 1001);
|
||||
assert.strictEqual(urlState.hash.arbitrary, undefined);
|
||||
|
|
@ -174,18 +172,21 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, 1001);
|
||||
assert.containsOnce(target, ".modal .test_client_action");
|
||||
await nextTick();
|
||||
});
|
||||
|
||||
QUnit.test("properly push state", async function (assert) {
|
||||
assert.expect(3);
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, 4);
|
||||
await nextTick();
|
||||
assert.deepEqual(webClient.env.services.router.current.hash, {
|
||||
action: 4,
|
||||
model: "partner",
|
||||
view_type: "kanban",
|
||||
});
|
||||
await doAction(webClient, 8);
|
||||
await nextTick();
|
||||
assert.deepEqual(webClient.env.services.router.current.hash, {
|
||||
action: 8,
|
||||
model: "pony",
|
||||
|
|
@ -202,7 +203,6 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
});
|
||||
|
||||
QUnit.test("push state after action is loaded, not before", async function (assert) {
|
||||
assert.expect(2);
|
||||
const def = makeDeferred();
|
||||
const mockRPC = async function (route, args) {
|
||||
if (args.method === "web_search_read") {
|
||||
|
|
@ -233,15 +233,16 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
};
|
||||
const webClient = await createWebClient({ serverData, mockRPC });
|
||||
await doAction(webClient, 8);
|
||||
await nextTick();
|
||||
assert.deepEqual(webClient.env.services.router.current.hash, {
|
||||
action: 8,
|
||||
model: "pony",
|
||||
view_type: "list",
|
||||
});
|
||||
await testUtils.dom.click($(target).find("tr.o_data_row:first"));
|
||||
await legacyExtraNextTick();
|
||||
// we make sure here that the list view is still in the dom
|
||||
assert.containsOnce(target, ".o_list_view", "there should still be a list view in dom");
|
||||
await nextTick();
|
||||
assert.deepEqual(webClient.env.services.router.current.hash, {
|
||||
action: 8,
|
||||
model: "pony",
|
||||
|
|
|
|||
|
|
@ -6,12 +6,20 @@ import { session } from "@web/session";
|
|||
import { ReportAction } from "@web/webclient/actions/reports/report_action";
|
||||
import { clearRegistryWithCleanup } from "@web/../tests/helpers/mock_env";
|
||||
import { makeFakeNotificationService } from "@web/../tests/helpers/mock_services";
|
||||
import { mockDownload, patchWithCleanup, getFixture, click } from "@web/../tests/helpers/utils";
|
||||
import {
|
||||
mockDownload,
|
||||
nextTick,
|
||||
patchWithCleanup,
|
||||
getFixture,
|
||||
click,
|
||||
} from "@web/../tests/helpers/utils";
|
||||
import {
|
||||
createWebClient,
|
||||
doAction,
|
||||
getActionManagerServerData,
|
||||
} from "@web/../tests/webclient/helpers";
|
||||
import { downloadReport } from "@web/webclient/actions/reports/utils";
|
||||
import { registerCleanup } from "../../../helpers/cleanup";
|
||||
|
||||
let serverData;
|
||||
let target;
|
||||
|
|
@ -23,6 +31,9 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
serverData = getActionManagerServerData();
|
||||
target = getFixture();
|
||||
clearRegistryWithCleanup(registry.category("main_components"));
|
||||
registerCleanup(() => {
|
||||
delete downloadReport.wkhtmltopdfStatusProm;
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.module("Report actions");
|
||||
|
|
@ -111,8 +122,8 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
"/web/webclient/load_menus",
|
||||
"/web/action/load",
|
||||
"/report/check_wkhtmltopdf",
|
||||
"notify",
|
||||
"/report/download",
|
||||
"notify",
|
||||
]);
|
||||
}
|
||||
);
|
||||
|
|
@ -149,7 +160,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
// attribute is removed right after)
|
||||
patchWithCleanup(ReportAction.prototype, {
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
super.setup(...arguments);
|
||||
this.env.services.rpc(this.reportUrl);
|
||||
this.reportUrl = "about:blank";
|
||||
},
|
||||
|
|
@ -161,7 +172,12 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
".o_content iframe",
|
||||
"should have opened the report client action"
|
||||
);
|
||||
assert.containsOnce(target, "button[title='Print']", "should have a print button");
|
||||
// the control panel has the content twice and a d-none class is toggled depending the screen size
|
||||
assert.containsOnce(
|
||||
target,
|
||||
":not(.d-none) > button[title='Print']",
|
||||
"should have a print button"
|
||||
);
|
||||
assert.verifySteps([
|
||||
"/web/webclient/load_menus",
|
||||
"/web/action/load",
|
||||
|
|
@ -202,7 +218,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
// attribute is removed right after)
|
||||
patchWithCleanup(ReportAction.prototype, {
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
super.setup(...arguments);
|
||||
this.env.services.rpc(this.reportUrl);
|
||||
this.reportUrl = "about:blank";
|
||||
},
|
||||
|
|
@ -253,7 +269,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
await doAction(webClient, 7);
|
||||
try {
|
||||
await doAction(webClient, 7);
|
||||
} catch (_e) {
|
||||
} catch {
|
||||
assert.step("error caught");
|
||||
}
|
||||
assert.verifySteps([
|
||||
|
|
@ -308,10 +324,14 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
});
|
||||
|
||||
QUnit.test("context is correctly passed to the client action report", async (assert) => {
|
||||
assert.expect(8);
|
||||
assert.expect(9);
|
||||
|
||||
mockDownload((options) => {
|
||||
assert.step(options.url);
|
||||
assert.deepEqual(
|
||||
options.data.context,
|
||||
`{"lang":"en","uid":7,"tz":"taht","rabbia":"E Tarantella","active_ids":[99]}`
|
||||
);
|
||||
assert.deepEqual(JSON.parse(options.data.data), [
|
||||
"/report/pdf/ennio.morricone/99",
|
||||
"qweb-pdf",
|
||||
|
|
@ -330,7 +350,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
|
||||
patchWithCleanup(ReportAction.prototype, {
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
super.setup(...arguments);
|
||||
this.env.services.rpc(this.reportUrl);
|
||||
this.reportUrl = "about:blank";
|
||||
},
|
||||
|
|
@ -354,7 +374,11 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
assert.verifySteps([
|
||||
"/report/html/ennio.morricone/99?context=%7B%22lang%22%3A%22en%22%2C%22uid%22%3A7%2C%22tz%22%3A%22taht%22%7D",
|
||||
]);
|
||||
await click(target.querySelector("button[title='Print']"));
|
||||
await click(
|
||||
target.querySelector(
|
||||
".o_control_panel_main_buttons .d-none.d-xl-inline-flex button[title='Print']"
|
||||
)
|
||||
);
|
||||
assert.verifySteps(["/report/check_wkhtmltopdf", "/report/download"]);
|
||||
});
|
||||
|
||||
|
|
@ -363,7 +387,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
|
||||
patchWithCleanup(ReportAction.prototype, {
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
super.init(...arguments);
|
||||
this.reportUrl = "about:blank";
|
||||
},
|
||||
});
|
||||
|
|
@ -371,7 +395,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
const webClient = await createWebClient({ serverData });
|
||||
|
||||
await doAction(webClient, 12); // 12 is a html report action in serverData
|
||||
|
||||
await nextTick();
|
||||
const hash = webClient.router.current.hash;
|
||||
// used to put report.client_action in the url
|
||||
assert.strictEqual(hash.action === "report.client_action", false);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import testUtils from "web.test_utils";
|
||||
import core from "web.core";
|
||||
import AbstractAction from "web.AbstractAction";
|
||||
import testUtils from "@web/../tests/legacy/helpers/test_utils";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { click, getFixture, patchWithCleanup, makeDeferred, nextTick } from "../../helpers/utils";
|
||||
import { createWebClient, doAction, getActionManagerServerData } from "./../helpers";
|
||||
import { registerCleanup } from "../../helpers/cleanup";
|
||||
import { errorService } from "@web/core/errors/error_service";
|
||||
import { useService } from "@web/core/utils/hooks";
|
||||
import { ClientErrorDialog } from "@web/core/errors/error_dialogs";
|
||||
|
|
@ -149,8 +146,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
]);
|
||||
await testUtils.dom.click(`button[name="5"]`);
|
||||
assert.verifySteps([
|
||||
"/web/dataset/call_kw/partner/create",
|
||||
"/web/dataset/call_kw/partner/read",
|
||||
"/web/dataset/call_kw/partner/web_save",
|
||||
"/web/action/load",
|
||||
"/web/dataset/call_kw/partner/get_views",
|
||||
"/web/dataset/call_kw/partner/onchange",
|
||||
|
|
@ -158,40 +154,13 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
assert.containsOnce(document.body, ".modal");
|
||||
await testUtils.dom.click(`button[name="some_method"]`);
|
||||
assert.verifySteps([
|
||||
"/web/dataset/call_kw/partner/create",
|
||||
"/web/dataset/call_kw/partner/read",
|
||||
"/web/dataset/call_kw/partner/web_save",
|
||||
"/web/dataset/call_button",
|
||||
"/web/dataset/call_kw/partner/read",
|
||||
"/web/dataset/call_kw/partner/web_read",
|
||||
]);
|
||||
assert.containsNone(document.body, ".modal");
|
||||
});
|
||||
|
||||
QUnit.test('on_attach_callback is called for actions in target="new"', async function (assert) {
|
||||
assert.expect(3);
|
||||
const ClientAction = AbstractAction.extend({
|
||||
on_attach_callback: function () {
|
||||
assert.step("on_attach_callback");
|
||||
assert.containsOnce(
|
||||
document.body,
|
||||
".modal .o_test",
|
||||
"should have rendered the client action in a dialog"
|
||||
);
|
||||
},
|
||||
start: function () {
|
||||
this.$el.addClass("o_test");
|
||||
},
|
||||
});
|
||||
core.action_registry.add("test", ClientAction);
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, {
|
||||
tag: "test",
|
||||
target: "new",
|
||||
type: "ir.actions.client",
|
||||
});
|
||||
assert.verifySteps(["on_attach_callback"]);
|
||||
delete core.action_registry.map.test;
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
'footer buttons are updated when having another action in target "new"',
|
||||
async function (assert) {
|
||||
|
|
@ -218,32 +187,6 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
'buttons of client action in target="new" and transition to MVC action',
|
||||
async function (assert) {
|
||||
const ClientAction = AbstractAction.extend({
|
||||
renderButtons($target) {
|
||||
const button = document.createElement("button");
|
||||
button.setAttribute("class", "o_stagger_lee");
|
||||
$target[0].appendChild(button);
|
||||
},
|
||||
});
|
||||
core.action_registry.add("test", ClientAction);
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, {
|
||||
tag: "test",
|
||||
target: "new",
|
||||
type: "ir.actions.client",
|
||||
});
|
||||
assert.containsOnce(target, ".modal footer button.o_stagger_lee");
|
||||
assert.containsNone(target, '.modal footer button[special="save"]');
|
||||
await doAction(webClient, 25);
|
||||
assert.containsNone(target, ".modal footer button.o_stagger_lee");
|
||||
assert.containsOnce(target, '.modal footer button[special="save"]');
|
||||
delete core.action_registry.map.test;
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test(
|
||||
'button with confirm attribute in act_window action in target="new"',
|
||||
async function (assert) {
|
||||
|
|
@ -322,18 +265,8 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
});
|
||||
|
||||
QUnit.test("do not commit a dialog in error", async (assert) => {
|
||||
assert.expect(6);
|
||||
|
||||
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: () => {},
|
||||
});
|
||||
assert.expect(7);
|
||||
assert.expectErrors();
|
||||
|
||||
class ErrorClientAction extends Component {
|
||||
setup() {
|
||||
|
|
@ -359,6 +292,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
);
|
||||
} catch (e) {
|
||||
assert.strictEqual(e.cause.message, "my error");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -371,7 +305,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
const errorDialogOpened = makeDeferred();
|
||||
patchWithCleanup(ClientErrorDialog.prototype, {
|
||||
setup() {
|
||||
this._super(...arguments);
|
||||
super.setup(...arguments);
|
||||
onMounted(() => errorDialogOpened.resolve());
|
||||
},
|
||||
});
|
||||
|
|
@ -388,8 +322,9 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
assert.ok(
|
||||
target.querySelector(".modal-body .o_error_detail").textContent.includes("my error")
|
||||
);
|
||||
assert.verifyErrors(["my error"]);
|
||||
|
||||
await click(target, ".modal-footer button");
|
||||
await click(target, ".modal-footer .btn-primary");
|
||||
assert.containsNone(target, ".modal");
|
||||
|
||||
await doAction(webClient, {
|
||||
|
|
@ -408,7 +343,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
// execute an action in target="current"
|
||||
await doAction(webClient, 1);
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll(".breadcrumb-item")].map((i) => i.innerText),
|
||||
[...target.querySelectorAll(".o_breadcrumb span")].map((i) => i.innerText),
|
||||
["Partners Action 1"]
|
||||
);
|
||||
|
||||
|
|
@ -421,7 +356,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
type: "ir.actions.act_window",
|
||||
views: [[false, "list"]],
|
||||
});
|
||||
assert.containsNone(target, ".modal .breadcrumb");
|
||||
assert.containsNone(target, ".modal .o_breadcrumb");
|
||||
});
|
||||
|
||||
QUnit.test('call switchView in an action in target="new"', async function (assert) {
|
||||
|
|
@ -484,7 +419,10 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
res_model: "pony",
|
||||
type: "ir.actions.act_window",
|
||||
target: "new",
|
||||
views: [[false, "list"], [false, "form"]],
|
||||
views: [
|
||||
[false, "list"],
|
||||
[false, "form"],
|
||||
],
|
||||
});
|
||||
|
||||
// The list view has been opened in a dialog
|
||||
|
|
@ -534,10 +472,13 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
] = `<form><button name="1" type="action" class="oe_stat_button" /></form>`;
|
||||
const webClient = await createWebClient({ serverData });
|
||||
await doAction(webClient, 6);
|
||||
await nextTick(); // for the webclient to react and remove the navbar
|
||||
assert.isNotVisible(target.querySelector(".o_main_navbar"));
|
||||
await click(target.querySelector("button[name='1']"));
|
||||
await nextTick();
|
||||
assert.isNotVisible(target.querySelector(".o_main_navbar"));
|
||||
await click(target.querySelector(".breadcrumb li a"));
|
||||
await nextTick();
|
||||
assert.isNotVisible(target.querySelector(".o_main_navbar"));
|
||||
});
|
||||
|
||||
|
|
@ -553,6 +494,7 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
'<form><button name="24" type="action" class="oe_stat_button"/></form>';
|
||||
await createWebClient({ serverData });
|
||||
await nextTick(); // wait for the load state (default app)
|
||||
await nextTick(); // wait for the action to be mounted
|
||||
assert.containsOnce(target, "nav .o_menu_brand");
|
||||
assert.strictEqual(target.querySelector("nav .o_menu_brand").innerText, "MAIN APP");
|
||||
await click(target.querySelector("button[name='24']"));
|
||||
|
|
@ -575,9 +517,9 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
await doAction(webClient, 1);
|
||||
|
||||
assert.containsOnce(target, ".o_kanban_view");
|
||||
assert.containsOnce(target, ".breadcrumb-item");
|
||||
assert.containsOnce(target, ".o_breadcrumb span");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_control_panel .breadcrumb").textContent,
|
||||
target.querySelector(".o_control_panel .o_breadcrumb").textContent,
|
||||
"Partners Action 1"
|
||||
);
|
||||
|
||||
|
|
@ -590,9 +532,9 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
});
|
||||
|
||||
assert.containsOnce(target, ".o_list_view");
|
||||
assert.containsOnce(target, ".breadcrumb-item");
|
||||
assert.containsOnce(target, ".o_breadcrumb span");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_control_panel .breadcrumb").textContent,
|
||||
target.querySelector(".o_control_panel .o_breadcrumb").textContent,
|
||||
"Another Partner Action"
|
||||
);
|
||||
});
|
||||
|
|
@ -611,9 +553,9 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
});
|
||||
|
||||
assert.containsOnce(target, ".o_list_view");
|
||||
assert.containsOnce(target, ".breadcrumb-item");
|
||||
assert.containsOnce(target, ".o_breadcrumb span");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_control_panel .breadcrumb").textContent,
|
||||
target.querySelector(".o_control_panel .o_breadcrumb").textContent,
|
||||
"Partner Action"
|
||||
);
|
||||
|
||||
|
|
@ -621,9 +563,10 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
await click(target.querySelector(".o_data_row .o_data_cell"));
|
||||
|
||||
assert.containsOnce(target, ".o_form_view");
|
||||
assert.containsN(target, ".breadcrumb-item", 2);
|
||||
assert.containsOnce(target, "ol.breadcrumb");
|
||||
assert.containsOnce(target, ".o_breadcrumb span");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_control_panel .breadcrumb").textContent,
|
||||
target.querySelector(".o_control_panel .o_breadcrumb").textContent,
|
||||
"Partner ActionFirst record"
|
||||
);
|
||||
});
|
||||
|
|
@ -642,31 +585,34 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
});
|
||||
|
||||
assert.containsOnce(target, ".o_list_view");
|
||||
assert.containsOnce(target, ".breadcrumb-item");
|
||||
assert.containsOnce(target, ".o_breadcrumb span");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_control_panel .breadcrumb").textContent,
|
||||
target.querySelector(".o_control_panel .o_breadcrumb").textContent,
|
||||
"Partner Action"
|
||||
);
|
||||
|
||||
// open first record
|
||||
await click(target.querySelector(".o_data_row .o_data_cell"));
|
||||
assert.containsOnce(target, ".o_form_view");
|
||||
assert.containsN(target, ".breadcrumb-item", 2);
|
||||
assert.containsOnce(target, "ol.breadcrumb");
|
||||
assert.containsOnce(target, ".o_breadcrumb span");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_control_panel .breadcrumb").textContent,
|
||||
target.querySelector(".o_control_panel .o_breadcrumb").textContent,
|
||||
"Partner ActionFirst record"
|
||||
);
|
||||
|
||||
await doAction(webClient, 1);
|
||||
assert.containsOnce(target, ".o_kanban_view");
|
||||
assert.containsN(target, ".breadcrumb-item", 3);
|
||||
assert.containsOnce(target, "ol.breadcrumb");
|
||||
assert.containsOnce(target, ".o_breadcrumb span");
|
||||
|
||||
// go back to form view
|
||||
await click(target.querySelectorAll(".breadcrumb-item")[1]);
|
||||
await click(target.querySelector("ol.breadcrumb .o_back_button"));
|
||||
assert.containsOnce(target, ".o_form_view");
|
||||
assert.containsN(target, ".breadcrumb-item", 2);
|
||||
assert.containsOnce(target, "ol.breadcrumb");
|
||||
assert.containsOnce(target, ".o_breadcrumb span");
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_control_panel .breadcrumb").textContent,
|
||||
target.querySelector(".o_control_panel .o_breadcrumb").textContent,
|
||||
"Partner ActionFirst record"
|
||||
);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,31 +1,27 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { makeTestEnv } from "../../helpers/mock_env";
|
||||
import { makeFakeRouterService } from "../../helpers/mock_services";
|
||||
import { setupWebClientRegistries, doAction, getActionManagerServerData } from "./../helpers";
|
||||
import { patchWithCleanup } from "@web/../tests/helpers/utils";
|
||||
import { getFixture, patchWithCleanup } from "@web/../tests/helpers/utils";
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
|
||||
let target;
|
||||
let serverData;
|
||||
const serviceRegistry = registry.category("services");
|
||||
|
||||
QUnit.module("ActionManager", (hooks) => {
|
||||
hooks.beforeEach(() => {
|
||||
target = getFixture();
|
||||
serverData = getActionManagerServerData();
|
||||
});
|
||||
|
||||
QUnit.module("URL actions");
|
||||
|
||||
QUnit.test("execute an 'ir.actions.act_url' action with target 'self'", async (assert) => {
|
||||
serviceRegistry.add(
|
||||
"router",
|
||||
makeFakeRouterService({
|
||||
onRedirect(url) {
|
||||
assert.step(url);
|
||||
},
|
||||
})
|
||||
);
|
||||
patchWithCleanup(browser.location, {
|
||||
assign: (url) => {
|
||||
assert.step(url);
|
||||
},
|
||||
});
|
||||
setupWebClientRegistries();
|
||||
const env = await makeTestEnv({ serverData });
|
||||
await doAction(env, {
|
||||
|
|
@ -48,17 +44,14 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
await doAction(env, { type: "ir.actions.act_url" }, options);
|
||||
assert.verifySteps(["browser open", "onClose"]);
|
||||
});
|
||||
|
||||
|
||||
QUnit.test("execute an 'ir.actions.act_url' action with url javascript:", async (assert) => {
|
||||
assert.expect(1);
|
||||
serviceRegistry.add(
|
||||
"router",
|
||||
makeFakeRouterService({
|
||||
onRedirect(url) {
|
||||
assert.strictEqual(url, "/javascript:alert()");
|
||||
},
|
||||
})
|
||||
);
|
||||
assert.expect(2);
|
||||
patchWithCleanup(browser.location, {
|
||||
assign: (url) => {
|
||||
assert.step(url);
|
||||
},
|
||||
});
|
||||
setupWebClientRegistries();
|
||||
const env = await makeTestEnv({ serverData });
|
||||
await doAction(env, {
|
||||
|
|
@ -66,5 +59,23 @@ QUnit.module("ActionManager", (hooks) => {
|
|||
target: "self",
|
||||
url: "javascript:alert()",
|
||||
});
|
||||
assert.verifySteps(["/javascript:alert()"]);
|
||||
});
|
||||
|
||||
QUnit.test("execute an 'ir.actions.act_url' action with target 'download'", async (assert) => {
|
||||
patchWithCleanup(browser.location, {
|
||||
assign: (url) => {
|
||||
assert.step(url);
|
||||
},
|
||||
});
|
||||
setupWebClientRegistries();
|
||||
const env = await makeTestEnv({ serverData });
|
||||
await doAction(env, {
|
||||
type: "ir.actions.act_url",
|
||||
target: "download",
|
||||
url: "/my/test/url",
|
||||
});
|
||||
assert.containsNone(target, ".o_blockUI", "ui should not be blocked");
|
||||
assert.verifySteps(["/my/test/url"]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -8,12 +8,24 @@ import {
|
|||
patchWithCleanup,
|
||||
triggerEvent,
|
||||
} from "@web/../tests/helpers/utils";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { uiService } from "@web/core/ui/ui_service";
|
||||
import { hotkeyService } from "@web/core/hotkeys/hotkey_service";
|
||||
|
||||
import { scanBarcode, BarcodeDialog } from "@web/webclient/barcode/barcode_scanner";
|
||||
import { createWebClient } from "@web/../tests/webclient/helpers";
|
||||
import { dialogService } from "@web/core/dialog/dialog_service";
|
||||
import { overlayService } from "@web/core/overlay/overlay_service";
|
||||
|
||||
QUnit.module("Barcode scanner", {});
|
||||
|
||||
QUnit.test("Barcode scanner crop overlay", async (assert) => {
|
||||
registry.category("services").add("ui", uiService);
|
||||
registry.category("services").add("hotkey", hotkeyService);
|
||||
registry.category("services").add("dialog", dialogService);
|
||||
registry.category("services").add("overlay", overlayService);
|
||||
|
||||
const { env } = await createWebClient({});
|
||||
const firstBarcodeValue = "Odoo";
|
||||
const secondBarcodeValue = "O-CMD-TEST";
|
||||
|
||||
|
|
@ -60,17 +72,17 @@ QUnit.test("Barcode scanner crop overlay", async (assert) => {
|
|||
|
||||
patchWithCleanup(BarcodeDialog.prototype, {
|
||||
async isVideoReady() {
|
||||
return this._super(...arguments).then(() => {
|
||||
videoReady.resolve();
|
||||
});
|
||||
const result = await super.isVideoReady(...arguments);
|
||||
videoReady.resolve();
|
||||
return result;
|
||||
},
|
||||
onResize(overlayInfo) {
|
||||
assert.step(JSON.stringify(overlayInfo));
|
||||
return this._super(...arguments);
|
||||
return super.onResize(...arguments);
|
||||
},
|
||||
});
|
||||
|
||||
const firstBarcodeFound = scanBarcode();
|
||||
const firstBarcodeFound = scanBarcode(env);
|
||||
await videoReady;
|
||||
// Needed due to the change on the props in the Crop component
|
||||
await nextTick();
|
||||
|
|
@ -113,7 +125,7 @@ QUnit.test("Barcode scanner crop overlay", async (assert) => {
|
|||
barcodeToGenerate = secondBarcodeValue;
|
||||
videoReady = makeDeferred();
|
||||
|
||||
const secondBarcodeFound = scanBarcode();
|
||||
const secondBarcodeFound = scanBarcode(env);
|
||||
await videoReady;
|
||||
const secondValueScanned = await secondBarcodeFound;
|
||||
assert.strictEqual(
|
||||
|
|
@ -131,3 +143,80 @@ QUnit.test("Barcode scanner crop overlay", async (assert) => {
|
|||
"We should haves three resize event; one for the default position, another one for the all frame and the last one must be the same as the saved second position"
|
||||
);
|
||||
});
|
||||
|
||||
QUnit.test("Closing barcode scanner before camera loads should not throw an error", async (assert) => {
|
||||
registry.category("services").add("ui", uiService);
|
||||
registry.category("services").add("hotkey", hotkeyService);
|
||||
registry.category("services").add("dialog", dialogService);
|
||||
registry.category("services").add("overlay", overlayService);
|
||||
|
||||
const { env } = await createWebClient({});
|
||||
const cameraReady = makeDeferred();
|
||||
|
||||
patchWithCleanup(browser.navigator, {
|
||||
mediaDevices: {
|
||||
async getUserMedia() {
|
||||
await cameraReady;
|
||||
const canvas = document.createElement("canvas");
|
||||
return canvas.captureStream();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
scanBarcode(env);
|
||||
|
||||
await nextTick();
|
||||
assert.ok(
|
||||
document.querySelector(".modal-dialog.modal-dialog-centered"),
|
||||
"The barcode dialog should be open."
|
||||
);
|
||||
|
||||
const escapeEvent = new KeyboardEvent("keydown", { key: "Escape", bubbles: true });
|
||||
document.body.dispatchEvent(escapeEvent);
|
||||
await nextTick();
|
||||
|
||||
assert.notOk(
|
||||
document.querySelector(".modal-dialog.modal-dialog-centered"),
|
||||
"The barcode dialog should be closed."
|
||||
);
|
||||
|
||||
cameraReady.resolve();
|
||||
await nextTick();
|
||||
});
|
||||
|
||||
QUnit.test("Closing barcode scanner while video is loading should not cause errors", async (assert) => {
|
||||
registry.category("services").add("ui", uiService);
|
||||
registry.category("services").add("hotkey", hotkeyService);
|
||||
registry.category("services").add("dialog", dialogService);
|
||||
registry.category("services").add("overlay", overlayService);
|
||||
|
||||
const { env } = await createWebClient({});
|
||||
|
||||
patchWithCleanup(browser.navigator, {
|
||||
mediaDevices: {
|
||||
async getUserMedia() {
|
||||
const canvas = document.createElement("canvas");
|
||||
return canvas.captureStream();
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
scanBarcode(env);
|
||||
await nextTick();
|
||||
|
||||
assert.ok(
|
||||
document.querySelector(".modal-dialog.modal-dialog-centered"),
|
||||
"The barcode dialog should be open."
|
||||
);
|
||||
|
||||
const escapeEvent = new KeyboardEvent("keydown", { key: "Escape", bubbles: true });
|
||||
document.body.dispatchEvent(escapeEvent);
|
||||
await nextTick();
|
||||
|
||||
assert.notOk(
|
||||
document.querySelector(".modal-dialog.modal-dialog-centered"),
|
||||
"The barcode dialog should be closed."
|
||||
);
|
||||
|
||||
await nextTick();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,523 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { registry } from "@web/core/registry";
|
||||
import { createWebClient } from "./helpers";
|
||||
import {
|
||||
getFixture,
|
||||
makeDeferred,
|
||||
nextTick,
|
||||
patchDate,
|
||||
patchWithCleanup,
|
||||
} from "@web/../tests/helpers/utils";
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
import { ListRenderer } from "@web/views/list/list_renderer";
|
||||
import { onWillStart, onWillUpdateProps } from "@odoo/owl";
|
||||
import { errorService } from "@web/core/errors/error_service";
|
||||
import { makeServerError } from "@web/../tests/helpers/mock_server";
|
||||
|
||||
let serverData;
|
||||
let clickEverywhereDef;
|
||||
|
||||
QUnit.module("clickbot", (hooks) => {
|
||||
hooks.beforeEach(async function () {
|
||||
serverData = {
|
||||
models: {
|
||||
foo: {
|
||||
fields: {
|
||||
foo: { string: "Foo", type: "char" },
|
||||
bar: { string: "Bar", type: "boolean" },
|
||||
date: { string: "Some Date", type: "date" },
|
||||
},
|
||||
records: [
|
||||
{
|
||||
id: 1,
|
||||
bar: true,
|
||||
foo: "yop",
|
||||
date: "2017-01-25",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
bar: true,
|
||||
foo: "blip",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
bar: true,
|
||||
foo: "gnap",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
bar: false,
|
||||
foo: "blip",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
views: {
|
||||
"foo,false,search": `
|
||||
<search>
|
||||
<filter string="Not Bar" name="not bar" domain="[['bar','=',False]]"/>
|
||||
<filter string="Date" name="date" date="date"/>
|
||||
</search>`,
|
||||
"foo,false,list": `
|
||||
<list>
|
||||
<field name="foo" />
|
||||
</list>`,
|
||||
"foo,false,kanban": `
|
||||
<kanban class="o_kanban_test">
|
||||
<templates><t t-name="kanban-box">
|
||||
<div>
|
||||
<field name="foo"/>
|
||||
</div>
|
||||
</t></templates>
|
||||
</kanban>`,
|
||||
},
|
||||
actions: {
|
||||
1001: {
|
||||
id: 1001,
|
||||
name: "App1",
|
||||
res_model: "foo",
|
||||
type: "ir.actions.act_window",
|
||||
views: [
|
||||
[false, "list"],
|
||||
[false, "kanban"],
|
||||
],
|
||||
xml_id: "app1",
|
||||
},
|
||||
1002: {
|
||||
id: 1002,
|
||||
name: "App2 Menu 1",
|
||||
res_model: "foo",
|
||||
type: "ir.actions.act_window",
|
||||
views: [[false, "kanban"]],
|
||||
xml_id: "app2_menu1",
|
||||
},
|
||||
1022: {
|
||||
id: 1022,
|
||||
name: "App2 Menu 2",
|
||||
res_model: "foo",
|
||||
type: "ir.actions.act_window",
|
||||
views: [[false, "list"]],
|
||||
xml_id: "app2_menu2",
|
||||
},
|
||||
},
|
||||
menus: {
|
||||
root: { id: "root", children: [1, 2], name: "root", appID: "root" },
|
||||
1: { id: 1, children: [], name: "App1", appID: 1, actionID: 1001, xmlid: "app1" },
|
||||
2: {
|
||||
id: 2,
|
||||
children: [3, 4],
|
||||
name: "App2",
|
||||
appID: 2,
|
||||
actionID: 1002,
|
||||
xmlid: "app2",
|
||||
},
|
||||
3: {
|
||||
id: 3,
|
||||
children: [],
|
||||
name: "menu 1",
|
||||
appID: 2,
|
||||
actionID: 1002,
|
||||
xmlid: "app2_menu1",
|
||||
},
|
||||
4: {
|
||||
id: 4,
|
||||
children: [],
|
||||
name: "menu 2",
|
||||
appID: 2,
|
||||
actionID: 1022,
|
||||
xmlid: "app2_menu2",
|
||||
},
|
||||
},
|
||||
};
|
||||
registry.category("command_categories").add("view_switcher", {});
|
||||
});
|
||||
QUnit.test("clickbot clickeverywhere test", async (assert) => {
|
||||
patchDate(2017, 9, 8, 15, 35, 11); // October 8 2017, 15:35:11
|
||||
patchWithCleanup(browser, {
|
||||
console: {
|
||||
log: (msg) => {
|
||||
assert.step(msg);
|
||||
if (msg === "test successful") {
|
||||
clickEverywhereDef.resolve();
|
||||
}
|
||||
},
|
||||
error: (msg) => {
|
||||
assert.step(msg);
|
||||
clickEverywhereDef.resolve();
|
||||
},
|
||||
},
|
||||
});
|
||||
await createWebClient({ serverData });
|
||||
clickEverywhereDef = makeDeferred();
|
||||
window.clickEverywhere();
|
||||
await clickEverywhereDef;
|
||||
assert.verifySteps([
|
||||
"Clicking on: apps menu toggle button",
|
||||
"Testing app menu: app1",
|
||||
"Testing menu App1 app1",
|
||||
'Clicking on: menu item "App1"',
|
||||
"Testing 2 filters",
|
||||
'Clicking on: filter "Not Bar"',
|
||||
'Clicking on: filter "Date"',
|
||||
'Clicking on: filter option "October"',
|
||||
"Testing view switch: kanban",
|
||||
"Clicking on: kanban view switcher",
|
||||
"Testing 2 filters",
|
||||
'Clicking on: filter "Not Bar"',
|
||||
'Clicking on: filter "Date"',
|
||||
'Clicking on: filter option "October"',
|
||||
"Clicking on: apps menu toggle button",
|
||||
"Testing app menu: app2",
|
||||
"Testing menu App2 app2",
|
||||
'Clicking on: menu item "App2"',
|
||||
"Testing 2 filters",
|
||||
'Clicking on: filter "Not Bar"',
|
||||
'Clicking on: filter "Date"',
|
||||
'Clicking on: filter option "October"',
|
||||
"Testing menu menu 1 app2_menu1",
|
||||
'Clicking on: menu item "menu 1"',
|
||||
"Testing 2 filters",
|
||||
'Clicking on: filter "Not Bar"',
|
||||
'Clicking on: filter "Date"',
|
||||
'Clicking on: filter option "October"',
|
||||
"Testing menu menu 2 app2_menu2",
|
||||
'Clicking on: menu item "menu 2"',
|
||||
"Testing 2 filters",
|
||||
'Clicking on: filter "Not Bar"',
|
||||
'Clicking on: filter "Date"',
|
||||
'Clicking on: filter option "October"',
|
||||
"Clicking on: apps menu toggle button",
|
||||
"Successfully tested 2 apps",
|
||||
"Successfully tested 2 menus",
|
||||
"Successfully tested 0 modals",
|
||||
"Successfully tested 10 filters",
|
||||
"test successful",
|
||||
]);
|
||||
});
|
||||
|
||||
QUnit.test("clickbot clickeverywhere test (with dropdown menu)", async (assert) => {
|
||||
serverData.menus.root.children = [2];
|
||||
serverData.menus[2].children = [5];
|
||||
serverData.menus[5] = {
|
||||
id: 5,
|
||||
children: [3, 4],
|
||||
name: "a dropdown",
|
||||
appID: 2,
|
||||
xmlid: "app2_dropdown_menu",
|
||||
};
|
||||
|
||||
patchDate(2017, 9, 8, 15, 35, 11); // October 8 2017, 15:35:11
|
||||
patchWithCleanup(browser, {
|
||||
console: {
|
||||
log: (msg) => {
|
||||
assert.step(msg);
|
||||
if (msg === "test successful") {
|
||||
clickEverywhereDef.resolve();
|
||||
}
|
||||
},
|
||||
error: (msg) => {
|
||||
assert.step(msg);
|
||||
clickEverywhereDef.resolve();
|
||||
},
|
||||
},
|
||||
});
|
||||
await createWebClient({ serverData });
|
||||
await nextTick();
|
||||
assert.containsOnce(
|
||||
getFixture(),
|
||||
".o_menu_sections .o-dropdown .dropdown-toggle:contains(a dropdown)"
|
||||
);
|
||||
clickEverywhereDef = makeDeferred();
|
||||
window.clickEverywhere();
|
||||
await clickEverywhereDef;
|
||||
assert.verifySteps([
|
||||
"Clicking on: apps menu toggle button",
|
||||
"Testing app menu: app2",
|
||||
"Testing menu App2 app2",
|
||||
'Clicking on: menu item "App2"',
|
||||
"Testing 2 filters",
|
||||
'Clicking on: filter "Not Bar"',
|
||||
'Clicking on: filter "Date"',
|
||||
'Clicking on: filter option "October"',
|
||||
"Clicking on: menu toggler",
|
||||
"Testing menu menu 1 app2_menu1",
|
||||
'Clicking on: menu item "menu 1"',
|
||||
"Testing 2 filters",
|
||||
'Clicking on: filter "Not Bar"',
|
||||
'Clicking on: filter "Date"',
|
||||
'Clicking on: filter option "October"',
|
||||
"Clicking on: menu toggler",
|
||||
"Testing menu menu 2 app2_menu2",
|
||||
'Clicking on: menu item "menu 2"',
|
||||
"Testing 2 filters",
|
||||
'Clicking on: filter "Not Bar"',
|
||||
'Clicking on: filter "Date"',
|
||||
'Clicking on: filter option "October"',
|
||||
"Clicking on: apps menu toggle button",
|
||||
"Successfully tested 1 apps",
|
||||
"Successfully tested 2 menus",
|
||||
"Successfully tested 0 modals",
|
||||
"Successfully tested 6 filters",
|
||||
"test successful",
|
||||
]);
|
||||
});
|
||||
|
||||
QUnit.test("clickbot test waiting rpc after clicking filter", async (assert) => {
|
||||
let clickBotStarted = false;
|
||||
serverData.actions = {
|
||||
1001: {
|
||||
id: 1,
|
||||
name: "App1",
|
||||
res_model: "foo",
|
||||
type: "ir.actions.act_window",
|
||||
views: [[false, "list"]],
|
||||
},
|
||||
};
|
||||
serverData.menus = {
|
||||
root: { id: "root", children: [1], name: "root", appID: "root" },
|
||||
1: { id: 1, children: [], name: "App1", appID: 1, actionID: 1001, xmlid: "app1" },
|
||||
};
|
||||
patchWithCleanup(browser, {
|
||||
console: {
|
||||
log: (msg) => {
|
||||
if (msg === "test successful") {
|
||||
assert.step(msg);
|
||||
clickEverywhereDef.resolve();
|
||||
}
|
||||
},
|
||||
error: () => {
|
||||
clickEverywhereDef.resolve();
|
||||
},
|
||||
},
|
||||
});
|
||||
await createWebClient({
|
||||
serverData,
|
||||
mockRPC: async function (route, args) {
|
||||
if (args.method === "web_search_read") {
|
||||
if (clickBotStarted) {
|
||||
assert.step("web_search_read called");
|
||||
await new Promise((r) => setTimeout(r, 1000));
|
||||
assert.step("response");
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
clickBotStarted = true;
|
||||
|
||||
clickEverywhereDef = makeDeferred();
|
||||
window.clickEverywhere();
|
||||
await clickEverywhereDef;
|
||||
assert.verifySteps([
|
||||
"web_search_read called", // click on the App
|
||||
"response",
|
||||
"web_search_read called", // click on the Filter
|
||||
"response",
|
||||
"web_search_read called", // click on the Second Filter
|
||||
"response",
|
||||
"test successful",
|
||||
]);
|
||||
});
|
||||
|
||||
QUnit.test("clickbot show rpc error when an error dialog is detected", async (assert) => {
|
||||
patchDate(2024, 3, 10, 0, 0, 0);
|
||||
let clickBotStarted = false;
|
||||
registry.category("services").add("error", errorService);
|
||||
serverData.actions = {
|
||||
1001: {
|
||||
id: 1,
|
||||
name: "App1",
|
||||
res_model: "foo",
|
||||
type: "ir.actions.act_window",
|
||||
views: [[false, "list"]],
|
||||
},
|
||||
};
|
||||
serverData.menus = {
|
||||
root: { id: "root", children: [1], name: "root", appID: "root" },
|
||||
1: { id: 1, children: [], name: "App1", appID: 1, actionID: 1001, xmlid: "app1" },
|
||||
};
|
||||
patchWithCleanup(browser, {
|
||||
console: {
|
||||
log: (msg) => {
|
||||
if (msg === "test successful") {
|
||||
assert.step(msg);
|
||||
clickEverywhereDef.resolve();
|
||||
}
|
||||
},
|
||||
error: (msg) => {
|
||||
assert.step(msg.toString());
|
||||
clickEverywhereDef.resolve();
|
||||
},
|
||||
},
|
||||
});
|
||||
let id = 1;
|
||||
await createWebClient({
|
||||
serverData,
|
||||
mockRPC: async function (route, args) {
|
||||
if (args.method === "web_search_read") {
|
||||
if (clickBotStarted) {
|
||||
if (id === 3) {
|
||||
// click on the Second Filter
|
||||
throw makeServerError({
|
||||
message:
|
||||
"This is a server Error, it should be displayed in an error dialog",
|
||||
});
|
||||
}
|
||||
id++;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
clickBotStarted = true;
|
||||
|
||||
clickEverywhereDef = makeDeferred();
|
||||
window.clickEverywhere();
|
||||
await clickEverywhereDef;
|
||||
await nextTick();
|
||||
assert.verifySteps([
|
||||
'A RPC in error was detected, maybe it\'s related to the error dialog : {"data":{"id":6,"jsonrpc":"2.0","method":"call","params":{"model":"foo","method":"web_search_read","args":[],"kwargs":{"specification":{"foo":{}},"offset":0,"order":"","limit":80,"context":{"lang":"en","uid":7,"tz":"taht","bin_size":true},"count_limit":10001,"domain":["|",["bar","=",false],"&",["date",">=","2024-04-01"],["date","<=","2024-04-30"]]}}},"settings":{"silent":false},"error":{"name":"RPC_ERROR","type":"server","code":200,"data":{"name":"odoo.exceptions.UserError","debug":"traceback","arguments":[],"context":{}},"exceptionName":"odoo.exceptions.UserError","message":"This is a server Error, it should be displayed in an error dialog","errorEvent":{"isTrusted":true}}}',
|
||||
"Error while testing App1 app1",
|
||||
'Error: Error dialog detected<header class="modal-header"><h4 class="modal-title text-break">Odoo Error</h4><button type="button" class="btn-close" aria-label="Close" tabindex="-1"></button></header><footer class="modal-footer justify-content-around justify-content-md-start flex-wrap gap-1 w-100" style="order:2"><button class="btn btn-primary o-default-button">Close</button><button class="btn btn-secondary"><i class="fa fa-clipboard mr8"></i>Copy error to clipboard</button></footer><main class="modal-body"><div role="alert"><p class="text-prewrap"><p><b>An error occurred</b></p><p>Please use the copy button to report the error to your support service.</p></p><button class="btn btn-link">See details</button></div></main>',
|
||||
]);
|
||||
});
|
||||
|
||||
QUnit.test("clickbot test waiting render after clicking filter", async (assert) => {
|
||||
let clickBotStarted = false;
|
||||
serverData.actions = {
|
||||
1001: {
|
||||
id: 1,
|
||||
name: "App1",
|
||||
res_model: "foo",
|
||||
type: "ir.actions.act_window",
|
||||
views: [[false, "list"]],
|
||||
},
|
||||
};
|
||||
serverData.menus = {
|
||||
root: { id: "root", children: [1], name: "root", appID: "root" },
|
||||
1: { id: 1, children: [], name: "App1", appID: 1, actionID: 1001, xmlid: "app1" },
|
||||
};
|
||||
patchWithCleanup(browser, {
|
||||
console: {
|
||||
log: (msg) => {
|
||||
if (msg === "test successful") {
|
||||
assert.step(msg);
|
||||
clickEverywhereDef.resolve();
|
||||
}
|
||||
},
|
||||
error: () => {
|
||||
clickEverywhereDef.resolve();
|
||||
},
|
||||
},
|
||||
});
|
||||
patchWithCleanup(ListRenderer.prototype, {
|
||||
setup() {
|
||||
super.setup(...arguments);
|
||||
onWillStart(async () => {
|
||||
if (clickBotStarted) {
|
||||
assert.step("onWillStart called");
|
||||
await new Promise((r) => setTimeout(r, 1000));
|
||||
assert.step("response");
|
||||
}
|
||||
});
|
||||
onWillUpdateProps(async () => {
|
||||
if (clickBotStarted) {
|
||||
assert.step("onWillUpdateProps called");
|
||||
await new Promise((r) => setTimeout(r, 1000));
|
||||
assert.step("response");
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
await createWebClient({ serverData });
|
||||
clickBotStarted = true;
|
||||
|
||||
clickEverywhereDef = makeDeferred();
|
||||
window.clickEverywhere();
|
||||
await clickEverywhereDef;
|
||||
assert.verifySteps([
|
||||
"onWillStart called", // click on APP
|
||||
"response",
|
||||
"onWillUpdateProps called", // click on filter
|
||||
"response",
|
||||
"onWillUpdateProps called", // click on second filter
|
||||
"response",
|
||||
"test successful",
|
||||
]);
|
||||
});
|
||||
|
||||
QUnit.test("clickbot clickeverywhere menu modal", async (assert) => {
|
||||
patchDate(2017, 9, 8, 15, 35, 11); // October 8 2017, 15:35:11
|
||||
serverData.views["foo,false,form"] = `
|
||||
<form>
|
||||
<field name="foo"/>
|
||||
</form>
|
||||
`;
|
||||
serverData.actions["1099"] = {
|
||||
id: 1099,
|
||||
name: "Modal",
|
||||
res_model: "foo",
|
||||
type: "ir.actions.act_window",
|
||||
views: [[false, "form"]],
|
||||
view_mode: "form",
|
||||
target: "new",
|
||||
};
|
||||
serverData.menus = {
|
||||
root: { id: "root", children: [1, 2], name: "root", appID: "root" },
|
||||
1: { id: 1, children: [], name: "App1", appID: 1, actionID: 1001, xmlid: "app1" },
|
||||
2: {
|
||||
id: 2,
|
||||
children: [],
|
||||
name: "App Modal",
|
||||
appID: 2,
|
||||
actionID: 1099,
|
||||
xmlid: "test.modal",
|
||||
},
|
||||
};
|
||||
patchWithCleanup(browser, {
|
||||
console: {
|
||||
log: (msg) => {
|
||||
assert.step(msg);
|
||||
if (msg === "test successful") {
|
||||
clickEverywhereDef.resolve();
|
||||
}
|
||||
},
|
||||
error: (msg) => {
|
||||
assert.step(msg);
|
||||
clickEverywhereDef.resolve();
|
||||
},
|
||||
},
|
||||
});
|
||||
await createWebClient({ serverData });
|
||||
clickEverywhereDef = makeDeferred();
|
||||
window.clickEverywhere();
|
||||
await clickEverywhereDef;
|
||||
assert.verifySteps([
|
||||
"Clicking on: apps menu toggle button",
|
||||
"Testing app menu: app1",
|
||||
"Testing menu App1 app1",
|
||||
'Clicking on: menu item "App1"',
|
||||
"Testing 2 filters",
|
||||
'Clicking on: filter "Not Bar"',
|
||||
'Clicking on: filter "Date"',
|
||||
'Clicking on: filter option "October"',
|
||||
"Testing view switch: kanban",
|
||||
"Clicking on: kanban view switcher",
|
||||
"Testing 2 filters",
|
||||
'Clicking on: filter "Not Bar"',
|
||||
'Clicking on: filter "Date"',
|
||||
'Clicking on: filter option "October"',
|
||||
"Clicking on: apps menu toggle button",
|
||||
"Testing app menu: test.modal",
|
||||
"Testing menu App Modal test.modal",
|
||||
'Clicking on: menu item "App Modal"',
|
||||
"Modal detected: App Modal test.modal",
|
||||
"Clicking on: modal close button",
|
||||
"Clicking on: apps menu toggle button",
|
||||
"Successfully tested 2 apps",
|
||||
"Successfully tested 0 menus",
|
||||
"Successfully tested 1 modals",
|
||||
"Successfully tested 4 filters",
|
||||
"test successful",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
import { ormService } from "@web/core/orm_service";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { session } from "@web/session";
|
||||
import { companyService } from "@web/webclient/company_service";
|
||||
import { makeTestEnv } from "../helpers/mock_env";
|
||||
import { patchWithCleanup } from "../helpers/utils";
|
||||
|
||||
const serviceRegistry = registry.category("services");
|
||||
|
||||
QUnit.module("company service");
|
||||
|
||||
QUnit.test("reload webclient when updating a res.company", async (assert) => {
|
||||
serviceRegistry.add("company", companyService);
|
||||
serviceRegistry.add("orm", ormService);
|
||||
serviceRegistry.add("action", {
|
||||
start(env) {
|
||||
return {
|
||||
doAction(action) {
|
||||
assert.step(action);
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
const env = await makeTestEnv();
|
||||
assert.verifySteps([]);
|
||||
await env.services.orm.read("res.company", [32]);
|
||||
assert.verifySteps([]);
|
||||
await env.services.orm.unlink("res.company", [32]);
|
||||
assert.verifySteps(["reload_context"]);
|
||||
await env.services.orm.unlink("notacompany", [32]);
|
||||
assert.verifySteps([]);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"do not reload webclient when updating a res.company, but there is an error",
|
||||
async (assert) => {
|
||||
serviceRegistry.add("company", companyService);
|
||||
serviceRegistry.add("orm", ormService);
|
||||
serviceRegistry.add("action", {
|
||||
start(env) {
|
||||
return {
|
||||
doAction(action) {
|
||||
assert.step(action);
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
const env = await makeTestEnv();
|
||||
assert.verifySteps([]);
|
||||
env.bus.trigger("RPC:RESPONSE", {
|
||||
data: { params: { model: "res.company", method: "write" } },
|
||||
settings: {},
|
||||
result: {},
|
||||
});
|
||||
assert.verifySteps(["reload_context"]);
|
||||
env.bus.trigger("RPC:RESPONSE", {
|
||||
data: { params: { model: "res.company", method: "write" } },
|
||||
settings: {},
|
||||
error: {},
|
||||
});
|
||||
assert.verifySteps([]);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("extract allowed company ids from url hash", async (assert) => {
|
||||
patchWithCleanup(session.user_companies, {
|
||||
allowed_companies: {
|
||||
1: { id: 1, name: "Company 1", sequence: 1, parent_id: false, child_ids: [] },
|
||||
2: { id: 2, name: "Company 2", sequence: 2, parent_id: false, child_ids: [] },
|
||||
3: { id: 3, name: "Company 3", sequence: 3, parent_id: false, child_ids: [] },
|
||||
},
|
||||
});
|
||||
|
||||
serviceRegistry.add("company", companyService);
|
||||
|
||||
Object.assign(browser.location, { hash: "cids=3-1" });
|
||||
let env = await makeTestEnv();
|
||||
assert.deepEqual(
|
||||
Object.values(env.services.company.allowedCompanies).map((c) => c.id),
|
||||
[1, 2, 3]
|
||||
);
|
||||
assert.deepEqual(env.services.company.activeCompanyIds, [3, 1]);
|
||||
assert.strictEqual(env.services.company.currentCompany.id, 3);
|
||||
|
||||
// backward compatibility
|
||||
registry.category("error_handlers").remove("accessErrorHandlerCompanies");
|
||||
Object.assign(browser.location, { hash: "cids=3%2C1" });
|
||||
env = await makeTestEnv();
|
||||
assert.deepEqual(env.services.company.activeCompanyIds, [3, 1]);
|
||||
assert.strictEqual(env.services.company.currentCompany.id, 3);
|
||||
});
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { ormService } from "@web/core/orm_service";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { currencies } from "@web/core/currency";
|
||||
import { currencyService } from "@web/webclient/currency_service";
|
||||
import { makeTestEnv } from "../helpers/mock_env";
|
||||
import { makeFakeRPCService } from "../helpers/mock_services";
|
||||
|
||||
const serviceRegistry = registry.category("services");
|
||||
|
||||
QUnit.module("currency service");
|
||||
|
||||
QUnit.test("reload currencies when updating a res.currency", async (assert) => {
|
||||
serviceRegistry.add("currency", currencyService);
|
||||
serviceRegistry.add("orm", ormService);
|
||||
const fakeRpc = makeFakeRPCService((route) => {
|
||||
assert.step(route);
|
||||
if (route === "/web/session/get_session_info") {
|
||||
return {
|
||||
uid: 1,
|
||||
currencies: {
|
||||
7: { symbol: "$", position: "before", digits: 2 },
|
||||
},
|
||||
};
|
||||
}
|
||||
});
|
||||
serviceRegistry.add("rpc", fakeRpc);
|
||||
const env = await makeTestEnv();
|
||||
assert.verifySteps([]);
|
||||
await env.services.orm.read("res.currency", [32]);
|
||||
assert.verifySteps(["/web/dataset/call_kw/res.currency/read"]);
|
||||
await env.services.orm.unlink("res.currency", [32]);
|
||||
assert.verifySteps([
|
||||
"/web/dataset/call_kw/res.currency/unlink",
|
||||
"/web/session/get_session_info",
|
||||
]);
|
||||
await env.services.orm.unlink("notcurrency", [32]);
|
||||
assert.verifySteps(["/web/dataset/call_kw/notcurrency/unlink"]);
|
||||
assert.deepEqual(Object.keys(currencies), ["7"]);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"do not reload webclient when updating a res.currency, but there is an error",
|
||||
async (assert) => {
|
||||
const fakeRpc = makeFakeRPCService((route) => {
|
||||
assert.step(route);
|
||||
});
|
||||
serviceRegistry.add("rpc", fakeRpc);
|
||||
serviceRegistry.add("currency", currencyService);
|
||||
|
||||
const env = await makeTestEnv();
|
||||
assert.verifySteps([]);
|
||||
env.bus.trigger("RPC:RESPONSE", {
|
||||
data: { params: { model: "res.currency", method: "write" } },
|
||||
settings: {},
|
||||
result: {},
|
||||
});
|
||||
assert.verifySteps(["/web/session/get_session_info"]);
|
||||
env.bus.trigger("RPC:RESPONSE", {
|
||||
data: { params: { model: "res.currency", method: "write" } },
|
||||
settings: {},
|
||||
error: {},
|
||||
});
|
||||
assert.verifySteps([]);
|
||||
}
|
||||
);
|
||||
|
|
@ -5,30 +5,12 @@ import { notificationService } from "@web/core/notifications/notification_servic
|
|||
import { ormService } from "@web/core/orm_service";
|
||||
import { popoverService } from "@web/core/popover/popover_service";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { legacyServiceProvider } from "@web/legacy/legacy_service_provider";
|
||||
import {
|
||||
makeLegacyNotificationService,
|
||||
mapLegacyEnvToWowlEnv,
|
||||
makeLegacySessionService,
|
||||
} from "@web/legacy/utils";
|
||||
import { makeLegacyActionManagerService } from "@web/legacy/backend_utils";
|
||||
import { generateLegacyLoadViewsResult } from "@web/legacy/legacy_load_views";
|
||||
import { viewService } from "@web/views/view_service";
|
||||
import { actionService } from "@web/webclient/actions/action_service";
|
||||
import { effectService } from "@web/core/effects/effect_service";
|
||||
import { hotkeyService } from "@web/core/hotkeys/hotkey_service";
|
||||
import { menuService } from "@web/webclient/menus/menu_service";
|
||||
import { WebClient } from "@web/webclient/webclient";
|
||||
// This import is needed because of it's sideeffects, for exemple :
|
||||
// web.test_utils easyload xml templates at line : 124:130.
|
||||
// Also it set the autocomplete delay time for the field Many2One at 0 for the tests at line : 132:137
|
||||
import "web.test_legacy";
|
||||
import AbstractService from "web.AbstractService";
|
||||
import ActionMenus from "web.ActionMenus";
|
||||
import basicFields from "web.basic_fields";
|
||||
import Registry from "web.Registry";
|
||||
import core from "web.core";
|
||||
import makeTestEnvironment from "web.test_env";
|
||||
import { registerCleanup } from "../helpers/cleanup";
|
||||
import { makeTestEnv } from "../helpers/mock_env";
|
||||
import {
|
||||
|
|
@ -37,26 +19,19 @@ import {
|
|||
makeFakeLocalizationService,
|
||||
makeFakeRouterService,
|
||||
makeFakeHTTPService,
|
||||
makeFakeBarcodeService,
|
||||
makeFakeUserService,
|
||||
} from "../helpers/mock_services";
|
||||
import {
|
||||
getFixture,
|
||||
legacyExtraNextTick,
|
||||
mount,
|
||||
nextTick,
|
||||
patchWithCleanup,
|
||||
} from "../helpers/utils";
|
||||
import session from "web.session";
|
||||
import LegacyMockServer from "web.MockServer";
|
||||
import Widget from "web.Widget";
|
||||
import { getFixture, mount, nextTick } from "../helpers/utils";
|
||||
import { uiService } from "@web/core/ui/ui_service";
|
||||
import { ClientActionAdapter, ViewAdapter } from "@web/legacy/action_adapters";
|
||||
import { commandService } from "@web/core/commands/command_service";
|
||||
import { ConnectionAbortedError } from "@web/core/network/rpc_service";
|
||||
import { CustomFavoriteItem } from "@web/search/favorite_menu/custom_favorite_item";
|
||||
import { standaloneAdapter } from "web.OwlCompatibility";
|
||||
import { CustomFavoriteItem } from "@web/search/custom_favorite_item/custom_favorite_item";
|
||||
import { overlayService } from "@web/core/overlay/overlay_service";
|
||||
|
||||
import { Component, onMounted, xml } from "@odoo/owl";
|
||||
import { Component, xml } from "@odoo/owl";
|
||||
import { fieldService } from "@web/core/field_service";
|
||||
import { nameService } from "@web/core/name_service";
|
||||
import { datetimePickerService } from "@web/core/datetime/datetimepicker_service";
|
||||
|
||||
const actionRegistry = registry.category("actions");
|
||||
const serviceRegistry = registry.category("services");
|
||||
|
|
@ -83,16 +58,19 @@ export function setupWebClientRegistries() {
|
|||
}
|
||||
const services = {
|
||||
action: () => actionService,
|
||||
barcode: () => makeFakeBarcodeService(),
|
||||
command: () => commandService,
|
||||
dialog: () => dialogService,
|
||||
effect: () => effectService,
|
||||
field: () => fieldService,
|
||||
hotkey: () => hotkeyService,
|
||||
http: () => makeFakeHTTPService(),
|
||||
legacy_service_provider: () => legacyServiceProvider,
|
||||
localization: () => makeFakeLocalizationService(),
|
||||
menu: () => menuService,
|
||||
name: () => nameService,
|
||||
notification: () => notificationService,
|
||||
orm: () => ormService,
|
||||
overlay: () => overlayService,
|
||||
popover: () => popoverService,
|
||||
router: () => makeFakeRouterService(),
|
||||
title: () => fakeTitleService,
|
||||
|
|
@ -100,6 +78,7 @@ export function setupWebClientRegistries() {
|
|||
user: () => makeFakeUserService(),
|
||||
view: () => viewService,
|
||||
company: () => fakeCompanyService,
|
||||
datetime_picker: () => datetimePickerService,
|
||||
};
|
||||
for (const serviceName in services) {
|
||||
if (!serviceRegistry.contains(serviceName)) {
|
||||
|
|
@ -108,139 +87,6 @@ export function setupWebClientRegistries() {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove this as soon as we drop the legacy support
|
||||
*/
|
||||
export async function addLegacyMockEnvironment(env, legacyParams = {}) {
|
||||
// setup a legacy env
|
||||
const dataManager = Object.assign(
|
||||
{
|
||||
load_action: (actionID, context) => {
|
||||
return env.services.rpc("/web/action/load", {
|
||||
action_id: actionID,
|
||||
additional_context: context,
|
||||
});
|
||||
},
|
||||
load_views: async (params, options) => {
|
||||
let result = await env.services.rpc(`/web/dataset/call_kw/${params.model}`, {
|
||||
args: [],
|
||||
kwargs: {
|
||||
context: params.context,
|
||||
options: options,
|
||||
views: params.views_descr,
|
||||
},
|
||||
method: "get_views",
|
||||
model: params.model,
|
||||
});
|
||||
const { models, views: _views } = result;
|
||||
result = generateLegacyLoadViewsResult(params.model, _views, models);
|
||||
const views = result.fields_views;
|
||||
for (const [, viewType] of params.views_descr) {
|
||||
const fvg = views[viewType];
|
||||
fvg.viewFields = fvg.fields;
|
||||
fvg.fields = result.fields;
|
||||
}
|
||||
if (params.favoriteFilters && "search" in views) {
|
||||
views.search.favoriteFilters = params.favoriteFilters;
|
||||
}
|
||||
return views;
|
||||
},
|
||||
load_filters: (params) => {
|
||||
if (QUnit.config.debug) {
|
||||
console.log("[mock] load_filters", params);
|
||||
}
|
||||
return Promise.resolve([]);
|
||||
},
|
||||
},
|
||||
legacyParams.dataManager
|
||||
);
|
||||
|
||||
// clear the ActionMenus registry to prevent external code from doing unknown rpcs
|
||||
const actionMenusRegistry = ActionMenus.registry;
|
||||
ActionMenus.registry = new Registry();
|
||||
registerCleanup(() => (ActionMenus.registry = actionMenusRegistry));
|
||||
|
||||
let localSession;
|
||||
if (legacyParams && legacyParams.getTZOffset) {
|
||||
patchWithCleanup(session, {
|
||||
getTZOffset: legacyParams.getTZOffset,
|
||||
});
|
||||
localSession = { getTZOffset: legacyParams.getTZOffset };
|
||||
}
|
||||
|
||||
const baseEnv = { dataManager, bus: core.bus, session: localSession };
|
||||
const legacyEnv = makeTestEnvironment(Object.assign(baseEnv, legacyParams.env));
|
||||
|
||||
if (legacyParams.serviceRegistry) {
|
||||
const legacyServiceMap = core.serviceRegistry.map;
|
||||
core.serviceRegistry.map = legacyParams.serviceRegistry.map;
|
||||
// notification isn't a deployed service, but it is added by `makeTestEnvironment`.
|
||||
// Here, we want full control on the deployed services, so we simply remove it.
|
||||
delete legacyEnv.services.notification;
|
||||
AbstractService.prototype.deployServices(legacyEnv);
|
||||
registerCleanup(() => {
|
||||
core.serviceRegistry.map = legacyServiceMap;
|
||||
});
|
||||
}
|
||||
|
||||
Component.env = legacyEnv;
|
||||
mapLegacyEnvToWowlEnv(legacyEnv, env);
|
||||
function patchLegacySession() {
|
||||
const userContext = Object.getOwnPropertyDescriptor(session, "user_context");
|
||||
registerCleanup(() => {
|
||||
Object.defineProperty(session, "user_context", userContext);
|
||||
});
|
||||
}
|
||||
patchLegacySession();
|
||||
serviceRegistry.add("legacy_session", makeLegacySessionService(legacyEnv, session));
|
||||
// deploy the legacyActionManagerService (in Wowl env)
|
||||
const legacyActionManagerService = makeLegacyActionManagerService(legacyEnv);
|
||||
serviceRegistry.add("legacy_action_manager", legacyActionManagerService);
|
||||
serviceRegistry.add("legacy_notification", makeLegacyNotificationService(legacyEnv));
|
||||
// deploy wowl services into the legacy env.
|
||||
const wowlToLegacyServiceMappers = registry.category("wowlToLegacyServiceMappers").getEntries();
|
||||
for (const [legacyServiceName, wowlToLegacyServiceMapper] of wowlToLegacyServiceMappers) {
|
||||
serviceRegistry.add(legacyServiceName, wowlToLegacyServiceMapper(legacyEnv));
|
||||
}
|
||||
// patch DebouncedField delay
|
||||
const debouncedField = basicFields.DebouncedField;
|
||||
const initialDebouncedVal = debouncedField.prototype.DEBOUNCE;
|
||||
debouncedField.prototype.DEBOUNCE = 0;
|
||||
registerCleanup(() => (debouncedField.prototype.DEBOUNCE = initialDebouncedVal));
|
||||
|
||||
if (legacyParams.withLegacyMockServer) {
|
||||
const adapter = standaloneAdapter({ Component });
|
||||
registerCleanup(() => adapter.__owl__.app.destroy());
|
||||
adapter.env = legacyEnv;
|
||||
const W = Widget.extend({ do_push_state() {} });
|
||||
const widget = new W(adapter);
|
||||
const legacyMockServer = new LegacyMockServer(legacyParams.models, { widget });
|
||||
const originalRPC = env.services.rpc;
|
||||
const rpc = async (...args) => {
|
||||
try {
|
||||
return await originalRPC(...args);
|
||||
} catch (e) {
|
||||
if (e.message.includes("Unimplemented")) {
|
||||
return legacyMockServer._performRpc(...args);
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
};
|
||||
env.services.rpc = function () {
|
||||
let rejectFn;
|
||||
const rpcProm = new Promise((resolve, reject) => {
|
||||
rejectFn = reject;
|
||||
rpc(...arguments)
|
||||
.then(resolve)
|
||||
.catch(reject);
|
||||
});
|
||||
rpcProm.abort = () => rejectFn(new ConnectionAbortedError("XmlHttpRequestError abort"));
|
||||
return rpcProm;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method create a web client instance properly configured.
|
||||
*
|
||||
|
|
@ -252,77 +98,31 @@ export async function addLegacyMockEnvironment(env, legacyParams = {}) {
|
|||
export async function createWebClient(params) {
|
||||
setupWebClientRegistries();
|
||||
|
||||
// With the compatibility layer, the action manager keeps legacy alive if they
|
||||
// are still acessible from the breacrumbs. They are manually destroyed as soon
|
||||
// as they are no longer referenced in the stack. This works fine in production,
|
||||
// because the webclient is never destroyed. However, at the end of each test,
|
||||
// we destroy the webclient and expect every legacy that has been instantiated
|
||||
// to be destroyed. We thus need to manually destroy them here.
|
||||
const controllers = [];
|
||||
patchWithCleanup(ClientActionAdapter.prototype, {
|
||||
setup() {
|
||||
this._super();
|
||||
onMounted(() => {
|
||||
controllers.push(this.widget);
|
||||
});
|
||||
},
|
||||
});
|
||||
patchWithCleanup(ViewAdapter.prototype, {
|
||||
setup() {
|
||||
this._super();
|
||||
onMounted(() => {
|
||||
controllers.push(this.widget);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
const legacyParams = params.legacyParams;
|
||||
params.serverData = params.serverData || {};
|
||||
const models = params.serverData.models;
|
||||
if (legacyParams && legacyParams.withLegacyMockServer && models) {
|
||||
legacyParams.models = Object.assign({}, models);
|
||||
// In lagacy, data may not be sole models, but can contain some other variables
|
||||
// So we filter them out for our WOWL mockServer
|
||||
Object.entries(legacyParams.models).forEach(([k, v]) => {
|
||||
if (!(v instanceof Object) || !("fields" in v)) {
|
||||
delete models[k];
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const mockRPC = params.mockRPC || undefined;
|
||||
const env = await makeTestEnv({
|
||||
serverData: params.serverData,
|
||||
mockRPC,
|
||||
});
|
||||
await addLegacyMockEnvironment(env, legacyParams);
|
||||
|
||||
const WebClientClass = params.WebClientClass || WebClient;
|
||||
const target = params && params.target ? params.target : getFixture();
|
||||
const wc = await mount(WebClientClass, target, { env });
|
||||
odoo.__WOWL_DEBUG__ = { root: wc };
|
||||
target.classList.add("o_web_client"); // necessary for the stylesheet
|
||||
registerCleanup(() => {
|
||||
target.classList.remove("o_web_client");
|
||||
for (const controller of controllers) {
|
||||
if (!controller.isDestroyed()) {
|
||||
controller.destroy();
|
||||
}
|
||||
}
|
||||
});
|
||||
// Wait for visual changes caused by a potential loadState
|
||||
await nextTick();
|
||||
return wc;
|
||||
}
|
||||
|
||||
export async function doAction(env, ...args) {
|
||||
export function doAction(env, ...args) {
|
||||
if (env instanceof Component) {
|
||||
env = env.env;
|
||||
}
|
||||
try {
|
||||
await env.services.action.doAction(...args);
|
||||
} finally {
|
||||
await legacyExtraNextTick();
|
||||
}
|
||||
return env.services.action.doAction(...args);
|
||||
}
|
||||
|
||||
export async function loadState(env, state) {
|
||||
|
|
@ -333,10 +133,10 @@ export async function loadState(env, state) {
|
|||
// wait the asynchronous hashchange
|
||||
// (the event hashchange must be triggered in a nonBlocking stack)
|
||||
await nextTick();
|
||||
// wait for BlankComponent
|
||||
await nextTick();
|
||||
// wait for the regular rendering
|
||||
await nextTick();
|
||||
// wait for the legacy rendering below owl layer
|
||||
await legacyExtraNextTick();
|
||||
}
|
||||
|
||||
export function getActionManagerServerData() {
|
||||
|
|
@ -374,6 +174,7 @@ export function getActionManagerServerData() {
|
|||
xml_id: "action_3",
|
||||
name: "Partners",
|
||||
res_model: "partner",
|
||||
mobile_view_mode: "kanban",
|
||||
type: "ir.actions.act_window",
|
||||
views: [
|
||||
[false, "list"],
|
||||
|
|
@ -475,6 +276,15 @@ export function getActionManagerServerData() {
|
|||
type: "ir.actions.act_window",
|
||||
views: [[3, "form"]],
|
||||
},
|
||||
{
|
||||
id: 26,
|
||||
xml_id: "action_26",
|
||||
name: "Partner",
|
||||
res_model: "partner",
|
||||
target: "new",
|
||||
type: "ir.actions.act_window",
|
||||
views: [[false, "list"]],
|
||||
},
|
||||
{
|
||||
id: 1001,
|
||||
tag: "__test__client__action__",
|
||||
|
|
@ -533,8 +343,8 @@ export function getActionManagerServerData() {
|
|||
"partner,666,form": `<form>
|
||||
<header></header>
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box" modifiers="{}">
|
||||
<button class="oe_stat_button" type="action" name="1" icon="fa-star" context="{'default_partner': active_id}">
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button class="oe_stat_button" type="action" name="1" icon="fa-star" context="{'default_partner': id}">
|
||||
<field string="Partners" name="o2m" widget="statinfo"/>
|
||||
</button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
import { browser as originalBrowser } from "@web/core/browser/browser";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { uiService } from "@web/core/ui/ui_service";
|
||||
import { patch, unpatch } from "@web/core/utils/patch";
|
||||
import { LoadingIndicator } from "@web/webclient/loading_indicator/loading_indicator";
|
||||
import { makeTestEnv } from "../helpers/mock_env";
|
||||
import {
|
||||
|
|
@ -31,12 +30,14 @@ QUnit.module("LoadingIndicator", {
|
|||
},
|
||||
});
|
||||
|
||||
const payload = (id) => ({ data: { id }, settings: {} });
|
||||
|
||||
QUnit.test("displays the loading indicator in non debug mode", async (assert) => {
|
||||
const env = await makeTestEnv();
|
||||
await mount(LoadingIndicator, target, { env });
|
||||
let loadingIndicator = target.querySelector(".o_loading_indicator");
|
||||
assert.strictEqual(loadingIndicator, null, "the loading indicator should not be displayed");
|
||||
env.bus.trigger("RPC:REQUEST", 1);
|
||||
env.bus.trigger("RPC:REQUEST", payload(1));
|
||||
await nextTick();
|
||||
loadingIndicator = target.querySelector(".o_loading_indicator");
|
||||
assert.notStrictEqual(loadingIndicator, null, "the loading indicator should be displayed");
|
||||
|
|
@ -45,7 +46,7 @@ QUnit.test("displays the loading indicator in non debug mode", async (assert) =>
|
|||
"Loading",
|
||||
"the loading indicator should display 'Loading'"
|
||||
);
|
||||
env.bus.trigger("RPC:RESPONSE", 1);
|
||||
env.bus.trigger("RPC:RESPONSE", payload(1));
|
||||
await nextTick();
|
||||
loadingIndicator = target.querySelector(".o_loading_indicator");
|
||||
assert.strictEqual(loadingIndicator, null, "the loading indicator should not be displayed");
|
||||
|
|
@ -57,7 +58,7 @@ QUnit.test("displays the loading indicator for one rpc in debug mode", async (as
|
|||
await mount(LoadingIndicator, target, { env });
|
||||
let loadingIndicator = target.querySelector(".o_loading_indicator");
|
||||
assert.strictEqual(loadingIndicator, null, "the loading indicator should not be displayed");
|
||||
env.bus.trigger("RPC:REQUEST", 1);
|
||||
env.bus.trigger("RPC:REQUEST", payload(1));
|
||||
await nextTick();
|
||||
loadingIndicator = target.querySelector(".o_loading_indicator");
|
||||
assert.notStrictEqual(loadingIndicator, null, "the loading indicator should be displayed");
|
||||
|
|
@ -66,7 +67,7 @@ QUnit.test("displays the loading indicator for one rpc in debug mode", async (as
|
|||
"Loading (1)",
|
||||
"the loading indicator should indicate 1 request in progress"
|
||||
);
|
||||
env.bus.trigger("RPC:RESPONSE", 1);
|
||||
env.bus.trigger("RPC:RESPONSE", payload(1));
|
||||
await nextTick();
|
||||
loadingIndicator = target.querySelector(".o_loading_indicator");
|
||||
assert.strictEqual(loadingIndicator, null, "the loading indicator should not be displayed");
|
||||
|
|
@ -78,8 +79,8 @@ QUnit.test("displays the loading indicator for multi rpc in debug mode", async (
|
|||
await mount(LoadingIndicator, target, { env });
|
||||
let loadingIndicator = target.querySelector(".o_loading_indicator");
|
||||
assert.strictEqual(loadingIndicator, null, "the loading indicator should not be displayed");
|
||||
env.bus.trigger("RPC:REQUEST", 1);
|
||||
env.bus.trigger("RPC:REQUEST", 2);
|
||||
env.bus.trigger("RPC:REQUEST", payload(1));
|
||||
env.bus.trigger("RPC:REQUEST", payload(2));
|
||||
await nextTick();
|
||||
loadingIndicator = target.querySelector(".o_loading_indicator");
|
||||
assert.notStrictEqual(loadingIndicator, null, "the loading indicator should be displayed");
|
||||
|
|
@ -88,7 +89,7 @@ QUnit.test("displays the loading indicator for multi rpc in debug mode", async (
|
|||
"Loading (2)",
|
||||
"the loading indicator should indicate 2 requests in progress."
|
||||
);
|
||||
env.bus.trigger("RPC:REQUEST", 3);
|
||||
env.bus.trigger("RPC:REQUEST", payload(3));
|
||||
await nextTick();
|
||||
loadingIndicator = target.querySelector(".o_loading_indicator");
|
||||
assert.strictEqual(
|
||||
|
|
@ -96,7 +97,7 @@ QUnit.test("displays the loading indicator for multi rpc in debug mode", async (
|
|||
"Loading (3)",
|
||||
"the loading indicator should indicate 3 requests in progress."
|
||||
);
|
||||
env.bus.trigger("RPC:RESPONSE", 1);
|
||||
env.bus.trigger("RPC:RESPONSE", payload(1));
|
||||
await nextTick();
|
||||
loadingIndicator = target.querySelector(".o_loading_indicator");
|
||||
assert.strictEqual(
|
||||
|
|
@ -104,7 +105,7 @@ QUnit.test("displays the loading indicator for multi rpc in debug mode", async (
|
|||
"Loading (2)",
|
||||
"the loading indicator should indicate 2 requests in progress."
|
||||
);
|
||||
env.bus.trigger("RPC:REQUEST", 4);
|
||||
env.bus.trigger("RPC:REQUEST", payload(4));
|
||||
await nextTick();
|
||||
loadingIndicator = target.querySelector(".o_loading_indicator");
|
||||
assert.strictEqual(
|
||||
|
|
@ -112,8 +113,8 @@ QUnit.test("displays the loading indicator for multi rpc in debug mode", async (
|
|||
"Loading (3)",
|
||||
"the loading indicator should indicate 3 requests in progress."
|
||||
);
|
||||
env.bus.trigger("RPC:RESPONSE", 2);
|
||||
env.bus.trigger("RPC:RESPONSE", 3);
|
||||
env.bus.trigger("RPC:RESPONSE", payload(2));
|
||||
env.bus.trigger("RPC:RESPONSE", payload(3));
|
||||
await nextTick();
|
||||
loadingIndicator = target.querySelector(".o_loading_indicator");
|
||||
assert.strictEqual(
|
||||
|
|
@ -121,58 +122,12 @@ QUnit.test("displays the loading indicator for multi rpc in debug mode", async (
|
|||
"Loading (1)",
|
||||
"the loading indicator should indicate 1 request in progress."
|
||||
);
|
||||
env.bus.trigger("RPC:RESPONSE", 4);
|
||||
env.bus.trigger("RPC:RESPONSE", payload(4));
|
||||
await nextTick();
|
||||
loadingIndicator = target.querySelector(".o_loading_indicator");
|
||||
assert.strictEqual(loadingIndicator, null, "the loading indicator should not be displayed");
|
||||
});
|
||||
|
||||
QUnit.test("loading indicator blocks UI", async (assert) => {
|
||||
const env = await makeTestEnv();
|
||||
patch(originalBrowser, "mock.settimeout", {
|
||||
setTimeout: async (callback, delay) => {
|
||||
assert.step(`set timeout ${delay}`);
|
||||
await Promise.resolve();
|
||||
callback();
|
||||
},
|
||||
});
|
||||
const ui = env.services.ui;
|
||||
ui.bus.addEventListener("BLOCK", () => {
|
||||
assert.step("block");
|
||||
});
|
||||
ui.bus.addEventListener("UNBLOCK", () => {
|
||||
assert.step("unblock");
|
||||
});
|
||||
await mount(LoadingIndicator, target, { env });
|
||||
env.bus.trigger("RPC:REQUEST", 1);
|
||||
await nextTick();
|
||||
env.bus.trigger("RPC:RESPONSE", 1);
|
||||
await nextTick();
|
||||
assert.verifySteps(["set timeout 250", "set timeout 3000", "block", "unblock"]);
|
||||
unpatch(originalBrowser, "mock.settimeout");
|
||||
});
|
||||
|
||||
QUnit.test("loading indicator doesn't unblock ui if it didn't block it", async (assert) => {
|
||||
const env = await makeTestEnv();
|
||||
const { execRegisteredTimeouts } = mockTimeout();
|
||||
const ui = env.services.ui;
|
||||
ui.bus.on("BLOCK", null, () => {
|
||||
assert.step("block");
|
||||
});
|
||||
ui.bus.on("UNBLOCK", null, () => {
|
||||
assert.step("unblock");
|
||||
});
|
||||
await mount(LoadingIndicator, target, { env });
|
||||
env.bus.trigger("RPC:REQUEST", 1);
|
||||
execRegisteredTimeouts();
|
||||
env.bus.trigger("RPC:RESPONSE", 1);
|
||||
assert.verifySteps(["block", "unblock"]);
|
||||
env.bus.trigger("RPC:REQUEST", 2);
|
||||
env.bus.trigger("RPC:RESPONSE", 2);
|
||||
execRegisteredTimeouts();
|
||||
assert.verifySteps([]);
|
||||
});
|
||||
|
||||
QUnit.test("loading indicator is not displayed immediately", async (assert) => {
|
||||
const env = await makeTestEnv();
|
||||
const { advanceTime } = mockTimeout();
|
||||
|
|
@ -185,14 +140,14 @@ QUnit.test("loading indicator is not displayed immediately", async (assert) => {
|
|||
assert.step("unblock");
|
||||
});
|
||||
await mount(LoadingIndicator, target, { env });
|
||||
env.bus.trigger("RPC:REQUEST", 1);
|
||||
env.bus.trigger("RPC:REQUEST", payload(1));
|
||||
await nextTick();
|
||||
assert.containsNone(target, ".o_loading_indicator");
|
||||
await advanceTime(400);
|
||||
await nextTick();
|
||||
assert.containsOnce(target, ".o_loading_indicator");
|
||||
|
||||
env.bus.trigger("RPC:RESPONSE", 1);
|
||||
env.bus.trigger("RPC:RESPONSE", payload(1));
|
||||
await nextTick();
|
||||
assert.containsNone(target, ".o_loading_indicator");
|
||||
});
|
||||
|
|
|
|||
|
|
@ -19,6 +19,10 @@ import { session } from "@web/session";
|
|||
const serviceRegistry = registry.category("services");
|
||||
let target;
|
||||
|
||||
function toCIDS(...ids) {
|
||||
return `cids=${ids.join("-")}&_company_switching=1`;
|
||||
}
|
||||
|
||||
const ORIGINAL_TOGGLE_DELAY = MobileSwitchCompanyMenu.toggleDelay;
|
||||
async function createSwitchCompanyMenu(routerParams = {}, toggleDelay = 0) {
|
||||
patchWithCleanup(MobileSwitchCompanyMenu, { toggleDelay });
|
||||
|
|
@ -45,11 +49,12 @@ QUnit.module("MobileSwitchCompanyMenu", (hooks) => {
|
|||
target = getFixture();
|
||||
patchWithCleanup(session.user_companies, {
|
||||
allowed_companies: {
|
||||
1: { id: 1, name: "Hermit" },
|
||||
2: { id: 2, name: "Herman's" },
|
||||
3: { id: 3, name: "Heroes TM" },
|
||||
1: { id: 1, name: "Hermit", parent_id: false, child_ids: [] },
|
||||
2: { id: 2, name: "Herman's", parent_id: false, child_ids: [] },
|
||||
3: { id: 3, name: "Heroes TM", parent_id: false, child_ids: [] },
|
||||
},
|
||||
current_company: 1,
|
||||
disallowed_ancestor_companies: {},
|
||||
});
|
||||
serviceRegistry.add("ui", uiService);
|
||||
serviceRegistry.add("company", companyService);
|
||||
|
|
@ -105,7 +110,7 @@ QUnit.module("MobileSwitchCompanyMenu", (hooks) => {
|
|||
* [ ] Company 2
|
||||
* [ ] Company 3
|
||||
*/
|
||||
assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [1]);
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [1]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 1);
|
||||
assert.containsN(scMenuEl, "[data-company-id]", 3);
|
||||
assert.containsN(scMenuEl, "[data-company-id] .fa-check-square", 1);
|
||||
|
|
@ -120,7 +125,7 @@ QUnit.module("MobileSwitchCompanyMenu", (hooks) => {
|
|||
assert.containsN(scMenuEl, "[data-company-id] .fa-check-square", 2);
|
||||
assert.containsN(scMenuEl, "[data-company-id] .fa-square-o", 1);
|
||||
await prom;
|
||||
assert.verifySteps(["cids=1%2C2"]);
|
||||
assert.verifySteps(["cids=1-2&_company_switching=1"]);
|
||||
});
|
||||
|
||||
QUnit.test("can toggle multiple companies at once", async (assert) => {
|
||||
|
|
@ -139,7 +144,7 @@ QUnit.module("MobileSwitchCompanyMenu", (hooks) => {
|
|||
* [ ] Company 2
|
||||
* [ ] Company 3
|
||||
*/
|
||||
assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [1]);
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [1]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 1);
|
||||
assert.containsN(scMenuEl, "[data-company-id]", 3);
|
||||
assert.containsN(scMenuEl, "[data-company-id] .fa-check-square", 1);
|
||||
|
|
@ -158,7 +163,7 @@ QUnit.module("MobileSwitchCompanyMenu", (hooks) => {
|
|||
|
||||
assert.verifySteps([]);
|
||||
await prom; // await toggle promise
|
||||
assert.verifySteps(["cids=2%2C3"]);
|
||||
assert.verifySteps(["cids=2-3&_company_switching=1"]);
|
||||
});
|
||||
|
||||
QUnit.test("single company selected: toggling it off will keep it", async (assert) => {
|
||||
|
|
@ -178,7 +183,7 @@ QUnit.module("MobileSwitchCompanyMenu", (hooks) => {
|
|||
* [ ] Company 3
|
||||
*/
|
||||
assert.deepEqual(scMenu.env.services.router.current.hash, { cids: 1 });
|
||||
assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [1]);
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [1]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 1);
|
||||
assert.containsN(scMenuEl, "[data-company-id]", 3);
|
||||
assert.containsN(scMenuEl, "[data-company-id] .fa-check-square", 1);
|
||||
|
|
@ -190,8 +195,11 @@ QUnit.module("MobileSwitchCompanyMenu", (hooks) => {
|
|||
* [ ] Company 3
|
||||
*/
|
||||
await click(scMenuEl.querySelectorAll(".toggle_company")[0]);
|
||||
assert.deepEqual(scMenu.env.services.router.current.hash, { cids: 1 });
|
||||
assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [1]);
|
||||
assert.deepEqual(scMenu.env.services.router.current.hash, {
|
||||
cids: 1,
|
||||
_company_switching: 1,
|
||||
});
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [1]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 1);
|
||||
assert.containsN(scMenuEl, "[data-company-id] .fa-check-squarqe", 0);
|
||||
assert.containsN(scMenuEl, "[data-company-id] .fa-square-o", 3);
|
||||
|
|
@ -211,7 +219,7 @@ QUnit.module("MobileSwitchCompanyMenu", (hooks) => {
|
|||
* [ ] Company 2
|
||||
* [ ] Company 3
|
||||
*/
|
||||
assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [1]);
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [1]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 1);
|
||||
assert.containsN(scMenuEl, "[data-company-id]", 3);
|
||||
assert.containsN(scMenuEl, "[data-company-id] .fa-check-square", 1);
|
||||
|
|
@ -223,7 +231,7 @@ QUnit.module("MobileSwitchCompanyMenu", (hooks) => {
|
|||
* [ ] Company 3
|
||||
*/
|
||||
await click(scMenuEl.querySelectorAll(".log_into")[1]);
|
||||
assert.verifySteps(["cids=2"]);
|
||||
assert.verifySteps(["cids=2&_company_switching=1"]);
|
||||
});
|
||||
|
||||
QUnit.test("multi company mode: log into a non selected company", async (assert) => {
|
||||
|
|
@ -232,7 +240,7 @@ QUnit.module("MobileSwitchCompanyMenu", (hooks) => {
|
|||
function onPushState(url) {
|
||||
assert.step(url.split("#")[1]);
|
||||
}
|
||||
Object.assign(browser.location, { hash: "cids=3%2C1" });
|
||||
Object.assign(browser.location, { hash: "cids=3-1" });
|
||||
const scMenu = await createSwitchCompanyMenu({ onPushState });
|
||||
const scMenuEl = target.querySelector(".o_burger_menu_companies");
|
||||
|
||||
|
|
@ -241,7 +249,7 @@ QUnit.module("MobileSwitchCompanyMenu", (hooks) => {
|
|||
* [ ] Company 2
|
||||
* [x] **Company 3**
|
||||
*/
|
||||
assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [3, 1]);
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [3, 1]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 3);
|
||||
assert.containsN(scMenuEl, "[data-company-id]", 3);
|
||||
assert.containsN(scMenuEl, "[data-company-id] .fa-check-square", 2);
|
||||
|
|
@ -249,11 +257,11 @@ QUnit.module("MobileSwitchCompanyMenu", (hooks) => {
|
|||
|
||||
/**
|
||||
* [x] Company 1
|
||||
* [ ] Company 2 -> log into
|
||||
* [x] **Company 3**
|
||||
* [x] **Company 2** -> log into
|
||||
* [x] Company 3
|
||||
*/
|
||||
await click(scMenuEl.querySelectorAll(".log_into")[1]);
|
||||
assert.verifySteps(["cids=2%2C3%2C1"]);
|
||||
assert.verifySteps([toCIDS(2, 3, 1)]);
|
||||
});
|
||||
|
||||
QUnit.test("multi company mode: log into an already selected company", async (assert) => {
|
||||
|
|
@ -262,7 +270,7 @@ QUnit.module("MobileSwitchCompanyMenu", (hooks) => {
|
|||
function onPushState(url) {
|
||||
assert.step(url.split("#")[1]);
|
||||
}
|
||||
Object.assign(browser.location, { hash: "cids=2%2C3" });
|
||||
Object.assign(browser.location, { hash: "cids=2-3" });
|
||||
const scMenu = await createSwitchCompanyMenu({ onPushState });
|
||||
const scMenuEl = target.querySelector(".o_burger_menu_companies");
|
||||
|
||||
|
|
@ -271,7 +279,7 @@ QUnit.module("MobileSwitchCompanyMenu", (hooks) => {
|
|||
* [x] **Company 2**
|
||||
* [x] Company 3
|
||||
*/
|
||||
assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [2, 3]);
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [2, 3]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 2);
|
||||
assert.containsN(scMenuEl, "[data-company-id]", 3);
|
||||
assert.containsN(scMenuEl, "[data-company-id] .fa-check-square", 2);
|
||||
|
|
@ -279,11 +287,11 @@ QUnit.module("MobileSwitchCompanyMenu", (hooks) => {
|
|||
|
||||
/**
|
||||
* [ ] Company 1
|
||||
* [x] **Company 2**
|
||||
* [x] Company 3 -> log into
|
||||
* [x] Company 2
|
||||
* [x] **Company 3** -> log into
|
||||
*/
|
||||
await click(scMenuEl.querySelectorAll(".log_into")[2]);
|
||||
assert.verifySteps(["cids=3%2C2"]);
|
||||
assert.verifySteps([toCIDS(3, 2)]);
|
||||
});
|
||||
|
||||
QUnit.test("companies can be logged in even if some toggled within delay", async (assert) => {
|
||||
|
|
@ -300,20 +308,23 @@ QUnit.module("MobileSwitchCompanyMenu", (hooks) => {
|
|||
* [ ] Company 2
|
||||
* [ ] Company 3
|
||||
*/
|
||||
assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [1]);
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [1]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 1);
|
||||
assert.containsN(scMenuEl, "[data-company-id]", 3);
|
||||
assert.containsN(scMenuEl, "[data-company-id] .fa-check-square", 1);
|
||||
assert.containsN(scMenuEl, "[data-company-id] .fa-square-o", 2);
|
||||
|
||||
/**
|
||||
* [ ] **Company 1** -> toggled
|
||||
* [ ] Company 2 -> logged in
|
||||
* [ ] Company 3 -> toggled
|
||||
* [ ] **Company 1** -> 2) toggled
|
||||
* [x] Company 2 -> 3) logged in
|
||||
* [ ] Company 3 -> 1) toggled
|
||||
*/
|
||||
await click(scMenuEl.querySelectorAll(".toggle_company")[2]);
|
||||
await click(scMenuEl.querySelectorAll(".toggle_company")[0]);
|
||||
await click(scMenuEl.querySelectorAll(".log_into")[1]);
|
||||
assert.verifySteps(["cids=2"]);
|
||||
|
||||
// When "Company 2" is logged into, only one company is currently selected
|
||||
// so we treat it as single company mode
|
||||
assert.verifySteps([toCIDS(2)]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
/** @odoo-module **/
|
||||
|
||||
import { overlayService } from "@web/core/overlay/overlay_service";
|
||||
import { browser } from "@web/core/browser/browser";
|
||||
import { notificationService } from "@web/core/notifications/notification_service";
|
||||
import { menuService } from "@web/webclient/menus/menu_service";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { ormService } from "@web/core/orm_service";
|
||||
import { uiService } from "@web/core/ui/ui_service";
|
||||
import { viewService } from "@web/views/view_service";
|
||||
import { actionService } from "@web/webclient/actions/action_service";
|
||||
import { hotkeyService } from "@web/core/hotkeys/hotkey_service";
|
||||
import { NavBar } from "@web/webclient/navbar/navbar";
|
||||
|
|
@ -34,13 +33,12 @@ let target;
|
|||
QUnit.module("Navbar", {
|
||||
async beforeEach() {
|
||||
target = getFixture();
|
||||
serviceRegistry.add("overlay", overlayService);
|
||||
serviceRegistry.add("menu", menuService);
|
||||
serviceRegistry.add("action", actionService);
|
||||
serviceRegistry.add("notification", notificationService);
|
||||
serviceRegistry.add("hotkey", hotkeyService);
|
||||
serviceRegistry.add("ui", uiService);
|
||||
serviceRegistry.add("view", viewService); // #action-serv-leg-compat-js-class
|
||||
serviceRegistry.add("orm", ormService); // #action-serv-leg-compat-js-class
|
||||
systrayRegistry.add("addon.myitem", { Component: MySystrayItem });
|
||||
patchWithCleanup(browser, {
|
||||
setTimeout: (handler, delay, ...args) => handler(...args),
|
||||
|
|
@ -233,7 +231,7 @@ QUnit.test("navbar updates after adding a systray item", async (assert) => {
|
|||
systrayRegistry.add("addon.myitem2", { Component: MyItem2 });
|
||||
}
|
||||
});
|
||||
this._super();
|
||||
super.setup();
|
||||
},
|
||||
});
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -24,7 +24,9 @@ QUnit.module("SettingsUpgradeBoolean", (hooks) => {
|
|||
type: "form",
|
||||
arch: `
|
||||
<form js_class="base_settings">
|
||||
<field name="bar" widget="upgrade_boolean"/>
|
||||
<app string="CRM" name="crm">
|
||||
<field name="bar" widget="upgrade_boolean"/>
|
||||
</app>
|
||||
</form>`,
|
||||
serverData,
|
||||
resModel: "res.config.settings",
|
||||
|
|
@ -44,10 +46,11 @@ QUnit.module("SettingsUpgradeBoolean", (hooks) => {
|
|||
type: "form",
|
||||
arch: `
|
||||
<form js_class="base_settings">
|
||||
<div class="o_field">
|
||||
<field name="bar" widget="upgrade_boolean"/>
|
||||
</div>
|
||||
<div class="o_label"><label for="bar"/><div>Coucou</div></div>
|
||||
<app string="CRM" name="crm">
|
||||
<setting string="Coucou">
|
||||
<field name="bar" widget="upgrade_boolean"/>
|
||||
</setting>
|
||||
</app>
|
||||
</form>`,
|
||||
serverData,
|
||||
resModel: "res.config.settings",
|
||||
|
|
@ -60,12 +63,12 @@ QUnit.module("SettingsUpgradeBoolean", (hooks) => {
|
|||
);
|
||||
assert.containsOnce(
|
||||
target,
|
||||
".o_label .badge",
|
||||
".o_form_label .badge",
|
||||
"the upgrade badge should be inside the label section"
|
||||
);
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_label").textContent,
|
||||
"BarEnterpriseCoucou",
|
||||
target.querySelector(".o_form_label").textContent,
|
||||
"CoucouEnterprise",
|
||||
"the upgrade label should be inside the label section"
|
||||
);
|
||||
});
|
||||
|
|
@ -78,7 +81,9 @@ QUnit.module("SettingsUpgradeBoolean", (hooks) => {
|
|||
type: "form",
|
||||
arch: `
|
||||
<form js_class="base_settings">
|
||||
<field name="bar" widget="upgrade_boolean"/>
|
||||
<app string="CRM" name="crm">
|
||||
<field name="bar" widget="upgrade_boolean"/>
|
||||
</app>
|
||||
</form>`,
|
||||
serverData,
|
||||
resModel: "res.config.settings",
|
||||
|
|
@ -102,10 +107,11 @@ QUnit.module("SettingsUpgradeBoolean", (hooks) => {
|
|||
type: "form",
|
||||
arch: `
|
||||
<form js_class="base_settings">
|
||||
<div class="o_field">
|
||||
<field name="bar" widget="upgrade_boolean"/>
|
||||
</div>
|
||||
<div class="o_label"><label for="bar"/><div>Coucou</div></div>
|
||||
<app string="CRM" name="crm">
|
||||
<setting string="Coucou">
|
||||
<field name="bar" widget="upgrade_boolean"/>
|
||||
</setting>
|
||||
</app>
|
||||
</form>`,
|
||||
serverData,
|
||||
resModel: "res.config.settings",
|
||||
|
|
@ -118,12 +124,12 @@ QUnit.module("SettingsUpgradeBoolean", (hooks) => {
|
|||
);
|
||||
assert.containsNone(
|
||||
target,
|
||||
".o_label .badge",
|
||||
".o_form_label .badge",
|
||||
"the upgrade badge shouldn't be inside the label section"
|
||||
);
|
||||
assert.strictEqual(
|
||||
target.querySelector(".o_label").textContent,
|
||||
"BarCoucou",
|
||||
target.querySelector(".o_form_label").textContent,
|
||||
"Coucou",
|
||||
"the label shouldn't contains the upgrade label"
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,14 +35,21 @@ async function createSwitchCompanyMenu(routerParams = {}, toggleDelay = 0) {
|
|||
return scMenu;
|
||||
}
|
||||
|
||||
function toCIDS(...ids) {
|
||||
return `cids=${ids.join("-")}&_company_switching=1`;
|
||||
}
|
||||
|
||||
QUnit.module("SwitchCompanyMenu", (hooks) => {
|
||||
hooks.beforeEach(() => {
|
||||
patchWithCleanup(session.user_companies, {
|
||||
allowed_companies: {
|
||||
3: { id: 3, name: "Hermit", sequence: 1 },
|
||||
2: { id: 2, name: "Herman's", sequence: 2 },
|
||||
1: { id: 1, name: "Heroes TM", sequence: 3 },
|
||||
3: { id: 3, name: "Hermit", sequence: 1, parent_id: false, child_ids: [] },
|
||||
2: { id: 2, name: "Herman's", sequence: 2, parent_id: false, child_ids: [] },
|
||||
1: { id: 1, name: "Heroes TM", sequence: 3, parent_id: false, child_ids: [4, 5] },
|
||||
4: { id: 4, name: "Hercules", sequence: 4, parent_id: 1, child_ids: [] },
|
||||
5: { id: 5, name: "Hulk", sequence: 5, parent_id: 1, child_ids: [] },
|
||||
},
|
||||
disallowed_ancestor_companies: {},
|
||||
current_company: 3,
|
||||
});
|
||||
serviceRegistry.add("ui", uiService);
|
||||
|
|
@ -60,10 +67,10 @@ QUnit.module("SwitchCompanyMenu", (hooks) => {
|
|||
assert.strictEqual(target.querySelector("div.o_switch_company_menu").textContent, "Hermit");
|
||||
|
||||
await click(target.querySelector(".dropdown-toggle"));
|
||||
assert.containsN(target, ".toggle_company", 3);
|
||||
assert.containsN(target, ".log_into", 3);
|
||||
assert.containsN(target, ".toggle_company", 5);
|
||||
assert.containsN(target, ".log_into", 5);
|
||||
assert.containsOnce(target, ".fa-check-square");
|
||||
assert.containsN(target, ".fa-square-o", 2);
|
||||
assert.containsN(target, ".fa-square-o", 4);
|
||||
assert.strictEqual(
|
||||
target.querySelector(".fa-check-square").closest(".dropdown-item").textContent,
|
||||
"Hermit"
|
||||
|
|
@ -74,7 +81,7 @@ QUnit.module("SwitchCompanyMenu", (hooks) => {
|
|||
);
|
||||
assert.strictEqual(
|
||||
target.querySelector(".dropdown-menu").textContent,
|
||||
"HermitHerman'sHeroes TM"
|
||||
"HermitHerman'sHeroes TMHerculesHulk"
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -90,45 +97,53 @@ QUnit.module("SwitchCompanyMenu", (hooks) => {
|
|||
* [x] **Hermit**
|
||||
* [ ] Herman's
|
||||
* [ ] Heroes TM
|
||||
* [ ] Hercules
|
||||
* [ ] Hulk
|
||||
*/
|
||||
assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [3]);
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [3]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 3);
|
||||
await click(target.querySelector(".dropdown-toggle"));
|
||||
assert.containsN(target, "[data-company-id]", 3);
|
||||
assert.containsN(target, "[data-company-id]", 5);
|
||||
assert.containsN(target, "[data-company-id] .fa-check-square", 1);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 2);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 4);
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll("[data-company-id] .toggle_company")].map(
|
||||
(el) => el.ariaChecked
|
||||
[...target.querySelectorAll("[data-company-id] .toggle_company")].map((el) =>
|
||||
el.getAttribute("aria-checked")
|
||||
),
|
||||
["true", "false", "false"]
|
||||
["true", "false", "false", "false", "false"]
|
||||
);
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll("[data-company-id] .log_into")].map((el) => el.ariaPressed),
|
||||
["true", "false", "false"]
|
||||
[...target.querySelectorAll("[data-company-id] .log_into")].map((el) =>
|
||||
el.getAttribute("aria-pressed")
|
||||
),
|
||||
["true", "false", "false", "false", "false"]
|
||||
);
|
||||
|
||||
/**
|
||||
* [x] **Hermit**
|
||||
* [x] Herman's -> toggle
|
||||
* [ ] Heroes TM
|
||||
* [ ] Hercules
|
||||
* [ ] Hulk
|
||||
*/
|
||||
await click(target.querySelectorAll(".toggle_company")[1]);
|
||||
assert.containsOnce(target, ".dropdown-menu", "dropdown is still opened");
|
||||
assert.containsN(target, "[data-company-id] .fa-check-square", 2);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 1);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 3);
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll("[data-company-id] .toggle_company")].map(
|
||||
(el) => el.ariaChecked
|
||||
[...target.querySelectorAll("[data-company-id] .toggle_company")].map((el) =>
|
||||
el.getAttribute("aria-checked")
|
||||
),
|
||||
["true", "true", "false"]
|
||||
["true", "true", "false", "false", "false"]
|
||||
);
|
||||
assert.deepEqual(
|
||||
[...target.querySelectorAll("[data-company-id] .log_into")].map((el) => el.ariaPressed),
|
||||
["true", "false", "false"]
|
||||
[...target.querySelectorAll("[data-company-id] .log_into")].map((el) =>
|
||||
el.getAttribute("aria-pressed")
|
||||
),
|
||||
["true", "false", "false", "false", "false"]
|
||||
);
|
||||
await prom;
|
||||
assert.verifySteps(["cids=3%2C2"]);
|
||||
assert.verifySteps(["cids=3-2&_company_switching=1"]);
|
||||
});
|
||||
|
||||
QUnit.test("can toggle multiple companies at once", async (assert) => {
|
||||
|
|
@ -145,29 +160,33 @@ QUnit.module("SwitchCompanyMenu", (hooks) => {
|
|||
* [x] **Hermit**
|
||||
* [ ] Herman's
|
||||
* [ ] Heroes TM
|
||||
* [ ] Hercules
|
||||
* [ ] Hulk
|
||||
*/
|
||||
assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [3]);
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [3]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 3);
|
||||
await click(target.querySelector(".dropdown-toggle"));
|
||||
assert.containsN(target, "[data-company-id]", 3);
|
||||
assert.containsN(target, "[data-company-id]", 5);
|
||||
assert.containsN(target, "[data-company-id] .fa-check-square", 1);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 2);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 4);
|
||||
|
||||
/**
|
||||
* [ ] **Hermit** -> toggle all
|
||||
* [x] Herman's -> toggle all
|
||||
* [x] Heroes TM -> toggle all
|
||||
* [ ] Hermit -> toggle all
|
||||
* [x] **Herman's** -> toggle all
|
||||
* [x] Heroes TM -> toggle all
|
||||
* [ ] Hercules
|
||||
* [ ] Hulk
|
||||
*/
|
||||
await click(target.querySelectorAll(".toggle_company")[0]);
|
||||
await click(target.querySelectorAll(".toggle_company")[1]);
|
||||
await click(target.querySelectorAll(".toggle_company")[2]);
|
||||
assert.containsOnce(target, ".dropdown-menu", "dropdown is still opened");
|
||||
assert.containsN(target, "[data-company-id] .fa-check-square", 2);
|
||||
assert.containsN(target, "[data-company-id] .fa-check-square", 4);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 1);
|
||||
|
||||
assert.verifySteps([]);
|
||||
await prom; // await toggle promise
|
||||
assert.verifySteps(["cids=2%2C1"]);
|
||||
assert.verifySteps(["cids=2-1-4-5&_company_switching=1"]);
|
||||
});
|
||||
|
||||
QUnit.test("single company selected: toggling it off will keep it", async (assert) => {
|
||||
|
|
@ -184,27 +203,34 @@ QUnit.module("SwitchCompanyMenu", (hooks) => {
|
|||
* [x] **Hermit**
|
||||
* [ ] Herman's
|
||||
* [ ] Heroes TM
|
||||
* [ ] Hercules
|
||||
* [ ] Hulk
|
||||
*/
|
||||
assert.deepEqual(scMenu.env.services.router.current.hash, { cids: 3 });
|
||||
assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [3]);
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [3]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 3);
|
||||
await click(target.querySelector(".dropdown-toggle"));
|
||||
assert.containsN(target, "[data-company-id]", 3);
|
||||
assert.containsN(target, "[data-company-id]", 5);
|
||||
assert.containsN(target, "[data-company-id] .fa-check-square", 1);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 2);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 4);
|
||||
|
||||
/**
|
||||
* [ ] **Hermit** -> toggle off
|
||||
* [x] **Hermit** -> toggle off
|
||||
* [ ] Herman's
|
||||
* [ ] Heroes TM
|
||||
* [ ] Hercules
|
||||
* [ ] Hulk
|
||||
*/
|
||||
await click(target.querySelectorAll(".toggle_company")[0]);
|
||||
assert.deepEqual(scMenu.env.services.router.current.hash, { cids: 3 });
|
||||
assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [3]);
|
||||
assert.deepEqual(scMenu.env.services.router.current.hash, {
|
||||
cids: 3,
|
||||
_company_switching: 1,
|
||||
});
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [3]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 3);
|
||||
assert.containsOnce(target, ".dropdown-menu", "dropdown is still opened");
|
||||
assert.containsN(target, "[data-company-id] .fa-check-square", 0);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 3);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 5);
|
||||
});
|
||||
|
||||
QUnit.test("single company mode: companies can be logged in", async (assert) => {
|
||||
|
|
@ -219,53 +245,164 @@ QUnit.module("SwitchCompanyMenu", (hooks) => {
|
|||
* [x] **Hermit**
|
||||
* [ ] Herman's
|
||||
* [ ] Heroes TM
|
||||
* [ ] Hercules
|
||||
* [ ] Hulk
|
||||
*/
|
||||
assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [3]);
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [3]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 3);
|
||||
await click(target.querySelector(".dropdown-toggle"));
|
||||
assert.containsN(target, "[data-company-id]", 3);
|
||||
assert.containsN(target, "[data-company-id]", 5);
|
||||
assert.containsN(target, "[data-company-id] .fa-check-square", 1);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 2);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 4);
|
||||
|
||||
/**
|
||||
* [x] **Hermit**
|
||||
* [ ] Herman's -> log into
|
||||
* [ ] Hermit
|
||||
* [x] **Herman's** -> log into
|
||||
* [ ] Heroes TM
|
||||
* [ ] Hercules
|
||||
* [ ] Hulk
|
||||
*/
|
||||
await click(target.querySelectorAll(".log_into")[1]);
|
||||
assert.containsNone(target, ".dropdown-menu", "dropdown is directly closed");
|
||||
assert.verifySteps(["cids=2"]);
|
||||
assert.verifySteps([toCIDS(2)]);
|
||||
});
|
||||
|
||||
QUnit.test("single company mode: from company loginto branch", async (assert) => {
|
||||
assert.expect(8);
|
||||
const scMenu = await createSwitchCompanyMenu({
|
||||
onPushState: (url) => assert.step(url.split("#")[1]),
|
||||
});
|
||||
|
||||
/**
|
||||
* [x] **Hermit**
|
||||
* [ ] Herman's
|
||||
* [ ] Heroes TM
|
||||
* [ ] Hercules
|
||||
* [ ] Hulk
|
||||
*/
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [3]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 3);
|
||||
await click(target.querySelector(".dropdown-toggle"));
|
||||
assert.containsN(target, "[data-company-id]", 5);
|
||||
assert.containsN(target, "[data-company-id] .fa-check-square", 1);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 4);
|
||||
|
||||
/**
|
||||
* [ ] Hermit
|
||||
* [ ] Herman's
|
||||
* [x] **Heroes TM** -> log into
|
||||
* [x] Hercules
|
||||
* [x] Hulk
|
||||
*/
|
||||
await click(target.querySelectorAll(".log_into")[2]);
|
||||
assert.containsNone(target, ".dropdown-menu", "dropdown is directly closed");
|
||||
assert.verifySteps([toCIDS(1, 4, 5)]);
|
||||
});
|
||||
|
||||
QUnit.test("single company mode: from branch loginto company", async (assert) => {
|
||||
assert.expect(8);
|
||||
Object.assign(browser.location, { hash: toCIDS(1, 4, 5) });
|
||||
const scMenu = await createSwitchCompanyMenu({
|
||||
onPushState: (url) => assert.step(url.split("#")[1]),
|
||||
});
|
||||
|
||||
/**
|
||||
* [ ] Hermit
|
||||
* [ ] Herman's
|
||||
* [x] **Heroes TM**
|
||||
* [x] Hercules
|
||||
* [x] Hulk
|
||||
*/
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [1, 4, 5]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 1);
|
||||
await click(target.querySelector(".dropdown-toggle"));
|
||||
assert.containsN(target, "[data-company-id]", 5);
|
||||
assert.containsN(target, "[data-company-id] .fa-check-square", 3);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 2);
|
||||
|
||||
/**
|
||||
* [x] Hermit -> log into
|
||||
* [ ] Herman's
|
||||
* [ ] Heroes TM
|
||||
* [ ] Hercules
|
||||
* [ ] Hulk
|
||||
*/
|
||||
await click(target.querySelectorAll(".log_into")[0]);
|
||||
assert.containsNone(target, ".dropdown-menu", "dropdown is directly closed");
|
||||
assert.verifySteps([toCIDS(3)]);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"single company mode: from leaf (only one company in branch selected) loginto company",
|
||||
async (assert) => {
|
||||
assert.expect(8);
|
||||
Object.assign(browser.location, { hash: toCIDS(1) });
|
||||
|
||||
function onPushState(url) {
|
||||
assert.step(url.split("#")[1]);
|
||||
}
|
||||
const scMenu = await createSwitchCompanyMenu({ onPushState });
|
||||
|
||||
/**
|
||||
* [ ] Hermit
|
||||
* [ ] Herman's
|
||||
* [x] **Heroes TM**
|
||||
* [ ] Hercules
|
||||
* [ ] Hulk
|
||||
*/
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [1]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 1);
|
||||
await click(target.querySelector(".dropdown-toggle"));
|
||||
assert.containsN(target, "[data-company-id]", 5);
|
||||
assert.containsN(target, "[data-company-id] .fa-check-square", 1);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 4);
|
||||
|
||||
/**
|
||||
* [ ] Hermit
|
||||
* [x] **Herman's** -> log into
|
||||
* [ ] Heroes TM
|
||||
* [ ] Hercules
|
||||
* [ ] Hulk
|
||||
*/
|
||||
await click(target.querySelectorAll(".log_into")[1]);
|
||||
assert.containsNone(target, ".dropdown-menu", "dropdown is directly closed");
|
||||
assert.verifySteps([toCIDS(2)]);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("multi company mode: log into a non selected company", async (assert) => {
|
||||
assert.expect(8);
|
||||
|
||||
function onPushState(url) {
|
||||
assert.step(url.split("#")[1]);
|
||||
}
|
||||
Object.assign(browser.location, { hash: "cids=3%2C1" });
|
||||
Object.assign(browser.location, { hash: toCIDS(3, 1) });
|
||||
const scMenu = await createSwitchCompanyMenu({ onPushState });
|
||||
|
||||
/**
|
||||
* [x] Hermit
|
||||
* [ ] Herman's
|
||||
* [x] **Heroes TM**
|
||||
* [ ] Hercules
|
||||
* [ ] Hulk
|
||||
*/
|
||||
assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [3, 1]);
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [3, 1]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 3);
|
||||
await click(target.querySelector(".dropdown-toggle"));
|
||||
assert.containsN(target, "[data-company-id]", 3);
|
||||
assert.containsN(target, "[data-company-id]", 5);
|
||||
assert.containsN(target, "[data-company-id] .fa-check-square", 2);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 1);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 3);
|
||||
|
||||
/**
|
||||
* [x] Hermit
|
||||
* [ ] Herman's -> log into
|
||||
* [x] **Heroes TM**
|
||||
* [x] **Herman's** -> log into
|
||||
* [x] Heroes TM
|
||||
* [ ] Hercules
|
||||
* [ ] Hulk
|
||||
*/
|
||||
await click(target.querySelectorAll(".log_into")[1]);
|
||||
assert.containsNone(target, ".dropdown-menu", "dropdown is directly closed");
|
||||
assert.verifySteps(["cids=2%2C3%2C1"]);
|
||||
assert.verifySteps([toCIDS(2, 3, 1)]);
|
||||
});
|
||||
|
||||
QUnit.test("multi company mode: log into an already selected company", async (assert) => {
|
||||
|
|
@ -274,31 +411,71 @@ QUnit.module("SwitchCompanyMenu", (hooks) => {
|
|||
function onPushState(url) {
|
||||
assert.step(url.split("#")[1]);
|
||||
}
|
||||
Object.assign(browser.location, { hash: "cids=2%2C1" });
|
||||
Object.assign(browser.location, { hash: "cids=2-1" });
|
||||
const scMenu = await createSwitchCompanyMenu({ onPushState });
|
||||
|
||||
/**
|
||||
* [ ] Hermit
|
||||
* [x] **Herman's**
|
||||
* [x] Heroes TM
|
||||
* [ ] Hercules
|
||||
* [ ] Hulk
|
||||
*/
|
||||
assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [2, 1]);
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [2, 1]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 2);
|
||||
await click(target.querySelector(".dropdown-toggle"));
|
||||
assert.containsN(target, "[data-company-id]", 3);
|
||||
assert.containsN(target, "[data-company-id]", 5);
|
||||
assert.containsN(target, "[data-company-id] .fa-check-square", 2);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 1);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 3);
|
||||
|
||||
/**
|
||||
* [ ] Hermit
|
||||
* [x] **Herman's**
|
||||
* [x] Heroes TM -> log into
|
||||
* [x] Herman's
|
||||
* [x] **Heroes TM** -> log into
|
||||
* [x] Hercules
|
||||
* [x] Hulk
|
||||
*/
|
||||
await click(target.querySelectorAll(".log_into")[2]);
|
||||
assert.containsNone(target, ".dropdown-menu", "dropdown is directly closed");
|
||||
assert.verifySteps(["cids=1%2C2"]);
|
||||
assert.verifySteps([toCIDS(1, 2, 4, 5)]);
|
||||
});
|
||||
|
||||
QUnit.test(
|
||||
"multi company mode: switching company doesn't deselect already selected ones",
|
||||
async (assert) => {
|
||||
assert.expect(8);
|
||||
Object.assign(browser.location, { hash: toCIDS(1, 2, 4, 5) });
|
||||
const scMenu = await createSwitchCompanyMenu({
|
||||
onPushState: (url) => assert.step(url.split("#")[1]),
|
||||
});
|
||||
|
||||
/**
|
||||
* [ ] Hermit
|
||||
* [x] Herman's
|
||||
* [x] **Heroes TM**
|
||||
* [x] Hercules
|
||||
* [x] Hulk
|
||||
*/
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [1, 2, 4, 5]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 1);
|
||||
await click(target.querySelector(".dropdown-toggle"));
|
||||
assert.containsN(target, "[data-company-id]", 5);
|
||||
assert.containsN(target, "[data-company-id] .fa-check-square", 4);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 1);
|
||||
|
||||
/**
|
||||
* [ ] Hermit
|
||||
* [x] **Herman's** -> log into
|
||||
* [x] Heroes TM
|
||||
* [x] Hercules
|
||||
* [x] Hulk
|
||||
*/
|
||||
await click(target.querySelectorAll(".log_into")[1]);
|
||||
assert.containsNone(target, ".dropdown-menu", "dropdown is directly closed");
|
||||
assert.verifySteps([toCIDS(2, 1, 4, 5)]);
|
||||
}
|
||||
);
|
||||
|
||||
QUnit.test("companies can be logged in even if some toggled within delay", async (assert) => {
|
||||
assert.expect(8);
|
||||
|
||||
|
|
@ -311,8 +488,59 @@ QUnit.module("SwitchCompanyMenu", (hooks) => {
|
|||
* [x] **Hermit**
|
||||
* [ ] Herman's
|
||||
* [ ] Heroes TM
|
||||
* [ ] Hercules
|
||||
* [ ] Hulk
|
||||
*/
|
||||
assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [3]);
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [3]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 3);
|
||||
await click(target.querySelector(".dropdown-toggle"));
|
||||
assert.containsN(target, "[data-company-id]", 5);
|
||||
assert.containsN(target, "[data-company-id] .fa-check-square", 1);
|
||||
assert.containsN(target, "[data-company-id] .fa-square-o", 4);
|
||||
|
||||
/**
|
||||
* [ ] Hermit -> 2) toggled
|
||||
* [x] **Herman's** -> 3) logged in
|
||||
* [ ] Heroes TM -> 1) toggled
|
||||
* [ ] Hercules
|
||||
* [ ] Hulk
|
||||
*/
|
||||
await click(target.querySelectorAll(".toggle_company")[2]);
|
||||
await click(target.querySelectorAll(".toggle_company")[0]);
|
||||
await click(target.querySelectorAll(".log_into")[1]);
|
||||
assert.containsNone(target, ".dropdown-menu", "dropdown is directly closed");
|
||||
|
||||
// When "Herman's" is logged into, only one company is currently selected
|
||||
// so we treat it as single company mode
|
||||
assert.verifySteps([toCIDS(2)]);
|
||||
});
|
||||
|
||||
QUnit.test("disallowed companies in between allowed companies are not enabled", async (assert) => {
|
||||
assert.expect(8);
|
||||
|
||||
patchWithCleanup(session.user_companies, {
|
||||
allowed_companies: {
|
||||
1: { id: 1, name: "Parent", sequence: 1, parent_id: false, child_ids: [2] },
|
||||
2: { id: 2, name: "Child A", sequence: 2, parent_id: 1, child_ids: [3] },
|
||||
3: { id: 3, name: "Child B", sequence: 2, parent_id: 2, child_ids: [] },
|
||||
},
|
||||
disallowed_ancestor_companies: {
|
||||
2: { id: 2, name: "Child A", sequence: 2, parent_id: 1, child_ids: [3] },
|
||||
},
|
||||
current_company: 3,
|
||||
});
|
||||
|
||||
function onPushState(url) {
|
||||
assert.step(url.split("#")[1]);
|
||||
}
|
||||
const scMenu = await createSwitchCompanyMenu({ onPushState }, ORIGINAL_TOGGLE_DELAY);
|
||||
|
||||
/**
|
||||
* [ ] Parent
|
||||
* [ ] Child A
|
||||
* [x] Child B
|
||||
*/
|
||||
assert.deepEqual(scMenu.env.services.company.activeCompanyIds, [3]);
|
||||
assert.strictEqual(scMenu.env.services.company.currentCompany.id, 3);
|
||||
await click(target.querySelector(".dropdown-toggle"));
|
||||
assert.containsN(target, "[data-company-id]", 3);
|
||||
|
|
@ -320,14 +548,15 @@ QUnit.module("SwitchCompanyMenu", (hooks) => {
|
|||
assert.containsN(target, "[data-company-id] .fa-square-o", 2);
|
||||
|
||||
/**
|
||||
* [ ] **Hermit** -> toggled
|
||||
* [ ] Herman's -> logged in
|
||||
* [ ] Heroes TM -> toggled
|
||||
* [x] Parent -> toggle
|
||||
* [ ] Child A
|
||||
* [x] Child B
|
||||
*/
|
||||
await click(target.querySelectorAll(".toggle_company")[2]);
|
||||
await click(target.querySelectorAll(".toggle_company")[0]);
|
||||
await click(target.querySelectorAll(".log_into")[1]);
|
||||
await click(target.querySelectorAll(".log_into")[0]);
|
||||
assert.containsNone(target, ".dropdown-menu", "dropdown is directly closed");
|
||||
assert.verifySteps(["cids=2"]);
|
||||
|
||||
// When "Herman's" is logged into, only one company is currently selected
|
||||
// so we treat it as single company mode
|
||||
assert.verifySteps([toCIDS(1, 3)]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -100,8 +100,6 @@ QUnit.test("can be rendered", async (assert) => {
|
|||
target.querySelector("img.o_user_avatar").dataset.src,
|
||||
"http://lordofthering/web/image?model=res.users&field=avatar_128&id=7"
|
||||
);
|
||||
assert.containsOnce(target, "span.oe_topbar_name");
|
||||
assert.strictEqual(target.querySelector(".oe_topbar_name").textContent, "Sauron");
|
||||
assert.containsNone(target, ".dropdown-menu .dropdown-item");
|
||||
await click(target.querySelector("button.dropdown-toggle"));
|
||||
assert.containsN(target, ".dropdown-menu .dropdown-item", 4);
|
||||
|
|
@ -132,8 +130,8 @@ QUnit.test("display the correct name in debug mode", async (assert) => {
|
|||
env = await makeTestEnv();
|
||||
await mount(UserMenu, target, { env });
|
||||
assert.containsOnce(target, "img.o_user_avatar");
|
||||
assert.containsOnce(target, "span.oe_topbar_name");
|
||||
assert.strictEqual(target.querySelector(".oe_topbar_name").textContent, "Sauron (test)");
|
||||
assert.containsOnce(target, "small.oe_topbar_name");
|
||||
assert.strictEqual(target.querySelector(".oe_topbar_name").textContent, "Sauron" + "test");
|
||||
});
|
||||
|
||||
QUnit.test("can execute the callback of settings", async (assert) => {
|
||||
|
|
|
|||
|
|
@ -2,12 +2,10 @@
|
|||
|
||||
import { dialogService } from "@web/core/dialog/dialog_service";
|
||||
import { notificationService } from "@web/core/notifications/notification_service";
|
||||
import { ormService } from "@web/core/orm_service";
|
||||
import { popoverService } from "@web/core/popover/popover_service";
|
||||
import { registry } from "@web/core/registry";
|
||||
import { ormService } from "@web/core/orm_service";
|
||||
import { uiService } from "@web/core/ui/ui_service";
|
||||
import { viewService } from "@web/views/view_service";
|
||||
import { legacyServiceProvider } from "@web/legacy/legacy_service_provider";
|
||||
import { actionService } from "@web/webclient/actions/action_service";
|
||||
import { hotkeyService } from "@web/core/hotkeys/hotkey_service";
|
||||
import { menuService } from "@web/webclient/menus/menu_service";
|
||||
|
|
@ -26,17 +24,15 @@ let target;
|
|||
QUnit.module("WebClient", {
|
||||
async beforeEach() {
|
||||
serviceRegistry
|
||||
.add("orm", ormService)
|
||||
.add("action", actionService)
|
||||
.add("dialog", dialogService)
|
||||
.add("hotkey", hotkeyService)
|
||||
.add("legacy_service_provider", legacyServiceProvider)
|
||||
.add("menu", menuService)
|
||||
.add("notification", notificationService)
|
||||
.add("popover", popoverService)
|
||||
.add("title", fakeTitleService)
|
||||
.add("ui", uiService)
|
||||
.add("view", viewService) // #action-serv-leg-compat-js-class
|
||||
.add("orm", ormService); // #action-serv-leg-compat-js-class
|
||||
.add("ui", uiService);
|
||||
baseConfig = { activateMockServer: true };
|
||||
target = getFixture();
|
||||
},
|
||||
|
|
@ -66,7 +62,7 @@ QUnit.test("control-click propagation stopped on <a href/>", async (assert) => {
|
|||
patchWithCleanup(WebClient.prototype, {
|
||||
/** @param {MouseEvent} ev */
|
||||
onGlobalClick(ev) {
|
||||
this._super(ev);
|
||||
super.onGlobalClick(ev);
|
||||
if (ev.ctrlKey) {
|
||||
assert.ok(
|
||||
ev.defaultPrevented === false,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue