The initial motivation of this commit was to ensure that the qunit test suite doesn't stop when an error is thrown in a test, which could happen if the error was thrown "sufficiently close to the end of the test". Indeed, the "unhandledrejection" event being async, it was sometimes triggered after the end of the test, when the service registry was already reset, and the check of the presence of the error service was wrong, so the error event wasn't default prevented (e.g. await makeView(...) and the view crashes at render time). This led us to rework in more depth the way we deal with errors in tests. Here are a few behaviors we want (probably not exhaustive): - an error in a test must never end the suite (executed in py) - an error in a test must always make the test fail, except if the error is expected in the scenario, which one must be able to state - a test must always wait for potential unhandledrejection events to be triggered before ending. - ideally, we don't want to have to deal with unhandledrejection in each test throwing an error (in order to prevent the suite to stop) To achieve this, we come with the following solution. We introduce a new assertion method, "expectToThrow" which allows to state that during the test, we expect errors to be thrown. It takes a list of error messages that will be compared at the end of the test with the errors that have been thrown during the test. If they differ, a qunit failure is pushed and the test fails. If an error occurs in a test and "expectToThrow" hasn't been called, qunit is directly informed of the error and a failing assertion is done, make the test fail as well. If the error service isn't available in the test environment, we apply the logic above when an "error" or and "unhandledrejection" event is thrown. If the error service is available, we wrap the default handler (typically the one that handles everything that hasn't been handled by specific handlers, like tracebacks) and if we get to it, we apply the logic above. This means that one must call "expectToThrow" if - the error service isn't deployed, or - the thrown error is handled by the default handler, because it is something like a traceback (errors like UserError, ValidationError are graciously handled by the RPCErrorHandler) and thus never reach the default handler. In all cases, we prevent default the event such that the error doesn't make the python test end. Finally, to ensure that "undhandledrejection" events are handled before the test ends, we wait, in the qunit lib, for a setTimeout before ending the test, which ensures that all such events have been dispatched. closes odoo/odoo#137120 Related: odoo/enterprise#48211 Signed-off-by: Samuel Degueldre (sad) <sad@odoo.com>
620 lines
25 KiB
JavaScript
620 lines
25 KiB
JavaScript
/** @odoo-module **/
|
|
|
|
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 { errorService } from "@web/core/errors/error_service";
|
|
import { useService } from "@web/core/utils/hooks";
|
|
import { ClientErrorDialog } from "@web/core/errors/error_dialogs";
|
|
|
|
import { Component, onMounted, xml } from "@odoo/owl";
|
|
|
|
let serverData;
|
|
let target;
|
|
|
|
QUnit.module("ActionManager", (hooks) => {
|
|
hooks.beforeEach(() => {
|
|
serverData = getActionManagerServerData();
|
|
target = getFixture();
|
|
});
|
|
|
|
QUnit.module('Actions in target="new"');
|
|
|
|
QUnit.test('can execute act_window actions in target="new"', async function (assert) {
|
|
assert.expect(8);
|
|
const mockRPC = async (route, args) => {
|
|
assert.step((args && args.method) || route);
|
|
};
|
|
const webClient = await createWebClient({ serverData, mockRPC });
|
|
await doAction(webClient, 5);
|
|
assert.containsOnce(
|
|
document.body,
|
|
".o_technical_modal .o_form_view",
|
|
"should have rendered a form view in a modal"
|
|
);
|
|
assert.hasClass(
|
|
$(".o_technical_modal .modal-body")[0],
|
|
"o_act_window",
|
|
"dialog main element should have classname 'o_act_window'"
|
|
);
|
|
assert.containsOnce(
|
|
document.body,
|
|
".o_technical_modal .o_form_view .o_form_editable",
|
|
"form view should be in edit mode"
|
|
);
|
|
assert.verifySteps([
|
|
"/web/webclient/load_menus",
|
|
"/web/action/load",
|
|
"get_views",
|
|
"onchange",
|
|
]);
|
|
});
|
|
|
|
QUnit.test("chained action on_close", async function (assert) {
|
|
assert.expect(4);
|
|
function onClose(closeInfo) {
|
|
assert.strictEqual(closeInfo, "smallCandle");
|
|
assert.step("Close Action");
|
|
}
|
|
const webClient = await createWebClient({ serverData });
|
|
await doAction(webClient, 5, { onClose });
|
|
// a target=new action shouldn't activate the on_close
|
|
await doAction(webClient, 5);
|
|
assert.verifySteps([]);
|
|
// An act_window_close should trigger the on_close
|
|
await doAction(webClient, { type: "ir.actions.act_window_close", infos: "smallCandle" });
|
|
assert.verifySteps(["Close Action"]);
|
|
});
|
|
|
|
QUnit.test("footer buttons are moved to the dialog footer", async function (assert) {
|
|
assert.expect(3);
|
|
serverData.views["partner,false,form"] = `
|
|
<form>
|
|
<field name="display_name"/>
|
|
<footer>
|
|
<button string="Create" type="object" class="infooter"/>
|
|
</footer>
|
|
</form>
|
|
`;
|
|
const webClient = await createWebClient({ serverData });
|
|
await doAction(webClient, 5);
|
|
assert.containsNone(
|
|
$(".o_technical_modal .modal-body")[0],
|
|
"button.infooter",
|
|
"the button should not be in the body"
|
|
);
|
|
assert.containsOnce(
|
|
$(".o_technical_modal .modal-footer")[0],
|
|
"button.infooter",
|
|
"the button should be in the footer"
|
|
);
|
|
assert.containsOnce(
|
|
target,
|
|
".modal-footer button:not(.d-none)",
|
|
"the modal footer should only contain one visible button"
|
|
);
|
|
});
|
|
|
|
QUnit.test("Button with `close` attribute closes dialog", async function (assert) {
|
|
serverData.views = {
|
|
"partner,false,form": `
|
|
<form>
|
|
<header>
|
|
<button string="Open dialog" name="5" type="action"/>
|
|
</header>
|
|
</form>`,
|
|
"partner,view_ref,form": `
|
|
<form>
|
|
<footer>
|
|
<button string="I close the dialog" name="some_method" type="object" close="1"/>
|
|
</footer>
|
|
</form>`,
|
|
"partner,false,search": "<search></search>",
|
|
};
|
|
serverData.actions[4] = {
|
|
id: 4,
|
|
name: "Partners Action 4",
|
|
res_model: "partner",
|
|
type: "ir.actions.act_window",
|
|
views: [[false, "form"]],
|
|
};
|
|
serverData.actions[5] = {
|
|
id: 5,
|
|
name: "Create a Partner",
|
|
res_model: "partner",
|
|
target: "new",
|
|
type: "ir.actions.act_window",
|
|
views: [["view_ref", "form"]],
|
|
};
|
|
const mockRPC = async (route, args) => {
|
|
assert.step(route);
|
|
if (route === "/web/dataset/call_button" && args.method === "some_method") {
|
|
return {
|
|
tag: "display_notification",
|
|
type: "ir.actions.client",
|
|
};
|
|
}
|
|
};
|
|
const webClient = await createWebClient({ serverData, mockRPC });
|
|
assert.verifySteps(["/web/webclient/load_menus"]);
|
|
await doAction(webClient, 4);
|
|
assert.verifySteps([
|
|
"/web/action/load",
|
|
"/web/dataset/call_kw/partner/get_views",
|
|
"/web/dataset/call_kw/partner/onchange",
|
|
]);
|
|
await testUtils.dom.click(`button[name="5"]`);
|
|
assert.verifySteps([
|
|
"/web/dataset/call_kw/partner/web_save",
|
|
"/web/action/load",
|
|
"/web/dataset/call_kw/partner/get_views",
|
|
"/web/dataset/call_kw/partner/onchange",
|
|
]);
|
|
assert.containsOnce(document.body, ".modal");
|
|
await testUtils.dom.click(`button[name="some_method"]`);
|
|
assert.verifySteps([
|
|
"/web/dataset/call_kw/partner/web_save",
|
|
"/web/dataset/call_button",
|
|
"/web/dataset/call_kw/partner/web_read",
|
|
]);
|
|
assert.containsNone(document.body, ".modal");
|
|
});
|
|
|
|
QUnit.test(
|
|
'footer buttons are updated when having another action in target "new"',
|
|
async function (assert) {
|
|
serverData.views["partner,false,form"] = `
|
|
<form>
|
|
<field name="display_name"/>
|
|
<footer>
|
|
<button string="Create" type="object" class="infooter"/>
|
|
</footer>
|
|
</form>
|
|
`;
|
|
const webClient = await createWebClient({ serverData });
|
|
await doAction(webClient, 5);
|
|
assert.containsNone(target, '.o_technical_modal .modal-body button[special="save"]');
|
|
assert.containsNone(target, ".o_technical_modal .modal-body button.infooter");
|
|
assert.containsOnce(target, ".o_technical_modal .modal-footer button.infooter");
|
|
assert.containsOnce(target, ".o_technical_modal .modal-footer button:not(.d-none)");
|
|
await doAction(webClient, 25);
|
|
assert.containsNone(target, ".o_technical_modal .modal-body button.infooter");
|
|
assert.containsNone(target, ".o_technical_modal .modal-footer button.infooter");
|
|
assert.containsNone(target, '.o_technical_modal .modal-body button[special="save"]');
|
|
assert.containsOnce(target, '.o_technical_modal .modal-footer button[special="save"]');
|
|
assert.containsOnce(target, ".o_technical_modal .modal-footer button:not(.d-none)");
|
|
}
|
|
);
|
|
|
|
QUnit.test(
|
|
'button with confirm attribute in act_window action in target="new"',
|
|
async function (assert) {
|
|
serverData.actions[999] = {
|
|
id: 999,
|
|
name: "A window action",
|
|
res_model: "partner",
|
|
target: "new",
|
|
type: "ir.actions.act_window",
|
|
views: [[999, "form"]],
|
|
};
|
|
serverData.views["partner,999,form"] = `
|
|
<form>
|
|
<button name="method" string="Call method" type="object" confirm="Are you sure?"/>
|
|
</form>`;
|
|
serverData.views["partner,1000,form"] = `<form>Another action</form>`;
|
|
|
|
const mockRPC = (route, args) => {
|
|
if (args.method === "method") {
|
|
return Promise.resolve({
|
|
id: 1000,
|
|
name: "Another window action",
|
|
res_model: "partner",
|
|
target: "new",
|
|
type: "ir.actions.act_window",
|
|
views: [[1000, "form"]],
|
|
});
|
|
}
|
|
};
|
|
const webClient = await createWebClient({ serverData, mockRPC });
|
|
|
|
await doAction(webClient, 999);
|
|
|
|
assert.containsOnce(document.body, ".modal button[name=method]");
|
|
|
|
await testUtils.dom.click($(".modal button[name=method]"));
|
|
|
|
assert.containsN(document.body, ".modal", 2);
|
|
assert.strictEqual($(".modal:last .modal-body").text(), "Are you sure?");
|
|
|
|
await testUtils.dom.click($(".modal:last .modal-footer .btn-primary"));
|
|
// needs two renderings to close the ConfirmationDialog:
|
|
// - 1 to open the next dialog (the action in target="new")
|
|
// - 1 to close the ConfirmationDialog, once the next action is executed
|
|
await nextTick();
|
|
assert.containsOnce(document.body, ".modal");
|
|
assert.strictEqual(
|
|
target.querySelector(".modal main .o_content").innerText.trim(),
|
|
"Another action"
|
|
);
|
|
}
|
|
);
|
|
|
|
QUnit.test('actions in target="new" do not update page title', async function (assert) {
|
|
const mockedTitleService = {
|
|
start() {
|
|
return {
|
|
setParts({ action }) {
|
|
if (action) {
|
|
assert.step(action);
|
|
}
|
|
},
|
|
};
|
|
},
|
|
};
|
|
registry.category("services").add("title", mockedTitleService);
|
|
const webClient = await createWebClient({ serverData });
|
|
|
|
// sanity check: execute an action in target="current"
|
|
await doAction(webClient, 1);
|
|
assert.verifySteps(["Partners Action 1"]);
|
|
|
|
// execute an action in target="new"
|
|
await doAction(webClient, 5);
|
|
assert.verifySteps([]);
|
|
});
|
|
|
|
QUnit.test("do not commit a dialog in error", async (assert) => {
|
|
assert.expect(7);
|
|
assert.expectErrors();
|
|
|
|
class ErrorClientAction extends Component {
|
|
setup() {
|
|
throw new Error("my error");
|
|
}
|
|
}
|
|
ErrorClientAction.template = xml`<div/>`;
|
|
registry.category("actions").add("failing", ErrorClientAction);
|
|
|
|
class ClientActionTargetNew extends Component {}
|
|
ClientActionTargetNew.template = xml`<div class="my_action_new" />`;
|
|
registry.category("actions").add("clientActionNew", ClientActionTargetNew);
|
|
|
|
class ClientAction extends Component {
|
|
setup() {
|
|
this.action = useService("action");
|
|
}
|
|
async onClick() {
|
|
try {
|
|
await this.action.doAction(
|
|
{ type: "ir.actions.client", tag: "failing", target: "new" },
|
|
{ onClose: () => assert.step("failing dialog closed") }
|
|
);
|
|
} catch (e) {
|
|
assert.strictEqual(e.cause.message, "my error");
|
|
throw e;
|
|
}
|
|
}
|
|
}
|
|
ClientAction.template = xml`
|
|
<div class="my_action" t-on-click="onClick">
|
|
My Action
|
|
</div>`;
|
|
registry.category("actions").add("clientAction", ClientAction);
|
|
|
|
const errorDialogOpened = makeDeferred();
|
|
patchWithCleanup(ClientErrorDialog.prototype, {
|
|
setup() {
|
|
super.setup(...arguments);
|
|
onMounted(() => errorDialogOpened.resolve());
|
|
},
|
|
});
|
|
|
|
registry.category("services").add("error", errorService);
|
|
const webClient = await createWebClient({});
|
|
|
|
await doAction(webClient, { type: "ir.actions.client", tag: "clientAction" });
|
|
await click(target, ".my_action");
|
|
await errorDialogOpened;
|
|
|
|
assert.containsOnce(target, ".modal");
|
|
await click(target, ".modal-body button.btn-link");
|
|
assert.ok(
|
|
target.querySelector(".modal-body .o_error_detail").textContent.includes("my error")
|
|
);
|
|
assert.verifyErrors(["my error"]);
|
|
|
|
await click(target, ".modal-footer .btn-primary");
|
|
assert.containsNone(target, ".modal");
|
|
|
|
await doAction(webClient, {
|
|
type: "ir.actions.client",
|
|
tag: "clientActionNew",
|
|
target: "new",
|
|
});
|
|
assert.containsOnce(target, ".modal .my_action_new");
|
|
|
|
assert.verifySteps([]);
|
|
});
|
|
|
|
QUnit.test('breadcrumbs of actions in target="new"', async function (assert) {
|
|
const webClient = await createWebClient({ serverData });
|
|
|
|
// execute an action in target="current"
|
|
await doAction(webClient, 1);
|
|
assert.deepEqual(
|
|
[...target.querySelectorAll(".o_breadcrumb span")].map((i) => i.innerText),
|
|
["Partners Action 1"]
|
|
);
|
|
|
|
// execute an action in target="new" and a list view (s.t. there is a control panel)
|
|
await doAction(webClient, {
|
|
xml_id: "action_5",
|
|
name: "Create a Partner",
|
|
res_model: "partner",
|
|
target: "new",
|
|
type: "ir.actions.act_window",
|
|
views: [[false, "list"]],
|
|
});
|
|
assert.containsNone(target, ".modal .o_breadcrumb");
|
|
});
|
|
|
|
QUnit.test('call switchView in an action in target="new"', async function (assert) {
|
|
const webClient = await createWebClient({ serverData });
|
|
|
|
// execute an action in target="current"
|
|
await doAction(webClient, 4);
|
|
assert.containsOnce(target, ".o_kanban_view");
|
|
|
|
// execute an action in target="new" and a list view (s.t. we can call switchView)
|
|
await doAction(webClient, {
|
|
xml_id: "action_5",
|
|
name: "Create a Partner",
|
|
res_model: "partner",
|
|
target: "new",
|
|
type: "ir.actions.act_window",
|
|
views: [[false, "list"]],
|
|
});
|
|
assert.containsOnce(target, ".modal .o_list_view");
|
|
assert.containsOnce(target, ".o_kanban_view");
|
|
|
|
// click on a record in the dialog -> should do nothing as we can't switch view
|
|
// in the dialog, and we don't want to switch view behind the dialog
|
|
await click(target.querySelector(".modal .o_data_row .o_data_cell"));
|
|
assert.containsOnce(target, ".modal .o_list_view");
|
|
assert.containsOnce(target, ".o_kanban_view");
|
|
});
|
|
|
|
QUnit.test("action with 'dialog_size' key in context", async function (assert) {
|
|
const action = {
|
|
name: "Some Action",
|
|
res_model: "partner",
|
|
type: "ir.actions.act_window",
|
|
target: "new",
|
|
views: [[false, "form"]],
|
|
};
|
|
const webClient = await createWebClient({ serverData });
|
|
|
|
await doAction(webClient, action);
|
|
assert.hasClass(target.querySelector(".o_dialog .modal-dialog"), "modal-lg");
|
|
|
|
await doAction(webClient, { ...action, context: { dialog_size: "small" } });
|
|
assert.hasClass(target.querySelector(".o_dialog .modal-dialog"), "modal-sm");
|
|
|
|
await doAction(webClient, { ...action, context: { dialog_size: "medium" } });
|
|
assert.hasClass(target.querySelector(".o_dialog .modal-dialog"), "modal-md");
|
|
|
|
await doAction(webClient, { ...action, context: { dialog_size: "large" } });
|
|
assert.hasClass(target.querySelector(".o_dialog .modal-dialog"), "modal-lg");
|
|
|
|
await doAction(webClient, { ...action, context: { dialog_size: "extra-large" } });
|
|
assert.hasClass(target.querySelector(".o_dialog .modal-dialog"), "modal-xl");
|
|
});
|
|
|
|
QUnit.test('click on record in list view action in target="new"', async function (assert) {
|
|
const webClient = await createWebClient({ serverData });
|
|
await doAction(webClient, 1001);
|
|
await doAction(webClient, {
|
|
name: "Favorite Ponies",
|
|
res_model: "pony",
|
|
type: "ir.actions.act_window",
|
|
target: "new",
|
|
views: [
|
|
[false, "list"],
|
|
[false, "form"],
|
|
],
|
|
});
|
|
|
|
// The list view has been opened in a dialog
|
|
assert.containsOnce(target, ".o_dialog .modal-dialog .o_list_view");
|
|
|
|
// click on a record in the dialog -> should do nothing as we can't switch view in the dialog
|
|
await click(target.querySelector(".modal .o_data_row .o_data_cell"));
|
|
assert.containsOnce(target, ".o_dialog .modal-dialog .o_list_view");
|
|
assert.containsNone(target, ".o_form_view");
|
|
});
|
|
|
|
QUnit.module('Actions in target="fullscreen"');
|
|
|
|
QUnit.test(
|
|
'correctly execute act_window actions in target="fullscreen"',
|
|
async function (assert) {
|
|
serverData.actions[1].target = "fullscreen";
|
|
const webClient = await createWebClient({ serverData });
|
|
await doAction(webClient, 1);
|
|
await nextTick(); // wait for the webclient template to be re-rendered
|
|
assert.containsOnce(target, ".o_control_panel", "should have rendered a control panel");
|
|
assert.containsOnce(target, ".o_kanban_view", "should have rendered a kanban view");
|
|
assert.containsNone(target, ".o_main_navbar");
|
|
}
|
|
);
|
|
|
|
QUnit.test('fullscreen on action change: back to a "current" action', async function (assert) {
|
|
serverData.actions[1].target = "fullscreen";
|
|
serverData.views[
|
|
"partner,false,form"
|
|
] = `<form><button name="1" type="action" class="oe_stat_button" /></form>`;
|
|
const webClient = await createWebClient({ serverData });
|
|
await doAction(webClient, 6);
|
|
assert.containsOnce(target, ".o_main_navbar");
|
|
await click(target.querySelector("button[name='1']"));
|
|
await nextTick(); // wait for the webclient template to be re-rendered
|
|
assert.containsNone(target, ".o_main_navbar");
|
|
await click(target.querySelector(".breadcrumb li a"));
|
|
await nextTick(); // wait for the webclient template to be re-rendered
|
|
assert.containsOnce(target, ".o_main_navbar");
|
|
});
|
|
|
|
QUnit.test('fullscreen on action change: all "fullscreen" actions', async function (assert) {
|
|
serverData.actions[6].target = "fullscreen";
|
|
serverData.views[
|
|
"partner,false,form"
|
|
] = `<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"));
|
|
});
|
|
|
|
QUnit.test(
|
|
'fullscreen on action change: back to another "current" action',
|
|
async function (assert) {
|
|
serverData.menus = {
|
|
root: { id: "root", children: [1], name: "root", appID: "root" },
|
|
1: { id: 1, children: [], name: "MAIN APP", appID: 1, actionID: 6 },
|
|
};
|
|
serverData.actions[1].target = "fullscreen";
|
|
serverData.views["partner,false,form"] =
|
|
'<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']"));
|
|
await nextTick(); // wait for the webclient template to be re-rendered
|
|
assert.containsOnce(target, "nav .o_menu_brand");
|
|
await click(target.querySelector("button[name='1']"));
|
|
await nextTick(); // wait for the webclient template to be re-rendered
|
|
assert.containsNone(target, "nav.o_main_navbar");
|
|
await click(target.querySelectorAll(".breadcrumb li a")[1]);
|
|
await nextTick(); // wait for the webclient template to be re-rendered
|
|
assert.containsOnce(target, "nav .o_menu_brand");
|
|
assert.strictEqual(target.querySelector("nav .o_menu_brand").innerText, "MAIN APP");
|
|
}
|
|
);
|
|
|
|
QUnit.module('Actions in target="main"');
|
|
|
|
QUnit.test('can execute act_window actions in target="main"', async function (assert) {
|
|
const webClient = await createWebClient({ serverData });
|
|
await doAction(webClient, 1);
|
|
|
|
assert.containsOnce(target, ".o_kanban_view");
|
|
assert.containsOnce(target, ".o_breadcrumb span");
|
|
assert.strictEqual(
|
|
target.querySelector(".o_control_panel .o_breadcrumb").textContent,
|
|
"Partners Action 1"
|
|
);
|
|
|
|
await doAction(webClient, {
|
|
name: "Another Partner Action",
|
|
res_model: "partner",
|
|
type: "ir.actions.act_window",
|
|
views: [[false, "list"]],
|
|
target: "main",
|
|
});
|
|
|
|
assert.containsOnce(target, ".o_list_view");
|
|
assert.containsOnce(target, ".o_breadcrumb span");
|
|
assert.strictEqual(
|
|
target.querySelector(".o_control_panel .o_breadcrumb").textContent,
|
|
"Another Partner Action"
|
|
);
|
|
});
|
|
|
|
QUnit.test('can switch view in an action in target="main"', async function (assert) {
|
|
const webClient = await createWebClient({ serverData });
|
|
await doAction(webClient, {
|
|
name: "Partner Action",
|
|
res_model: "partner",
|
|
type: "ir.actions.act_window",
|
|
views: [
|
|
[false, "list"],
|
|
[false, "form"],
|
|
],
|
|
target: "main",
|
|
});
|
|
|
|
assert.containsOnce(target, ".o_list_view");
|
|
assert.containsOnce(target, ".o_breadcrumb span");
|
|
assert.strictEqual(
|
|
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.containsOnce(target, "ol.breadcrumb");
|
|
assert.containsOnce(target, ".o_breadcrumb span");
|
|
assert.strictEqual(
|
|
target.querySelector(".o_control_panel .o_breadcrumb").textContent,
|
|
"Partner ActionFirst record"
|
|
);
|
|
});
|
|
|
|
QUnit.test('can restore an action in target="main"', async function (assert) {
|
|
const webClient = await createWebClient({ serverData });
|
|
await doAction(webClient, {
|
|
name: "Partner Action",
|
|
res_model: "partner",
|
|
type: "ir.actions.act_window",
|
|
views: [
|
|
[false, "list"],
|
|
[false, "form"],
|
|
],
|
|
target: "main",
|
|
});
|
|
|
|
assert.containsOnce(target, ".o_list_view");
|
|
assert.containsOnce(target, ".o_breadcrumb span");
|
|
assert.strictEqual(
|
|
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.containsOnce(target, "ol.breadcrumb");
|
|
assert.containsOnce(target, ".o_breadcrumb span");
|
|
assert.strictEqual(
|
|
target.querySelector(".o_control_panel .o_breadcrumb").textContent,
|
|
"Partner ActionFirst record"
|
|
);
|
|
|
|
await doAction(webClient, 1);
|
|
assert.containsOnce(target, ".o_kanban_view");
|
|
assert.containsOnce(target, "ol.breadcrumb");
|
|
assert.containsOnce(target, ".o_breadcrumb span");
|
|
|
|
// go back to form view
|
|
await click(target.querySelector("ol.breadcrumb .o_back_button"));
|
|
assert.containsOnce(target, ".o_form_view");
|
|
assert.containsOnce(target, "ol.breadcrumb");
|
|
assert.containsOnce(target, ".o_breadcrumb span");
|
|
assert.strictEqual(
|
|
target.querySelector(".o_control_panel .o_breadcrumb").textContent,
|
|
"Partner ActionFirst record"
|
|
);
|
|
});
|
|
});
|