+
+
![]()
+
+
+
+
+
diff --git a/addons/web/static/src/webclient/menus/menu_providers.js b/addons/web/static/src/webclient/menus/menu_providers.js
index 161a4709c8f..73c9a5b9526 100644
--- a/addons/web/static/src/webclient/menus/menu_providers.js
+++ b/addons/web/static/src/webclient/menus/menu_providers.js
@@ -40,6 +40,7 @@ commandProviderRegistry.add("menu", {
},
category: "menu_items",
name: menu.parents + " / " + menu.label,
+ href: menu.href || `#menu_id=${menu.id}&action_id=${menu.actionID}`,
});
});
}
@@ -63,6 +64,7 @@ commandProviderRegistry.add("menu", {
},
category: "apps",
name: menu.label,
+ href: menu.href || `#menu_id=${menu.id}&action_id=${menu.actionID}`,
props,
});
});
diff --git a/addons/web/static/tests/core/commands/command_palette_tests.js b/addons/web/static/tests/core/commands/command_palette_tests.js
index ed6057e09ba..8f44a7cdf84 100644
--- a/addons/web/static/tests/core/commands/command_palette_tests.js
+++ b/addons/web/static/tests/core/commands/command_palette_tests.js
@@ -832,7 +832,7 @@ QUnit.test("multi provider with categories", async (assert) => {
assert.deepEqual(
[
...target.querySelectorAll(
- ".o_command_category:nth-of-type(1) .o_command > div > span:first-child"
+ ".o_command_category:nth-of-type(1) .o_command > a > div > span:first-child"
),
].map((el) => el.textContent),
["Command1"]
@@ -840,7 +840,7 @@ QUnit.test("multi provider with categories", async (assert) => {
assert.deepEqual(
[
...target.querySelectorAll(
- ".o_command_category:nth-of-type(2) .o_command > div > span:first-child"
+ ".o_command_category:nth-of-type(2) .o_command > a > div > span:first-child"
),
].map((el) => el.textContent),
["Command2"]
@@ -848,7 +848,7 @@ QUnit.test("multi provider with categories", async (assert) => {
assert.deepEqual(
[
...target.querySelectorAll(
- ".o_command_category:nth-of-type(3) .o_command > div > span:first-child"
+ ".o_command_category:nth-of-type(3) .o_command > a > div > span:first-child"
),
].map((el) => el.textContent),
["Command3"]
@@ -865,7 +865,7 @@ QUnit.test("multi provider with categories", async (assert) => {
assert.deepEqual(
[
...target.querySelectorAll(
- ".o_command_category:nth-of-type(1) .o_command > div > span:first-child"
+ ".o_command_category:nth-of-type(1) .o_command > a > div > span:first-child"
),
].map((el) => el.textContent),
["Command6", "Command7"]
@@ -873,7 +873,7 @@ QUnit.test("multi provider with categories", async (assert) => {
assert.deepEqual(
[
...target.querySelectorAll(
- ".o_command_category:nth-of-type(2) .o_command > div > span:first-child"
+ ".o_command_category:nth-of-type(2) .o_command > a > div > span:first-child"
),
].map((el) => el.textContent),
["Command5"]
@@ -881,7 +881,7 @@ QUnit.test("multi provider with categories", async (assert) => {
assert.deepEqual(
[
...target.querySelectorAll(
- ".o_command_category:nth-of-type(3) .o_command > div > span:first-child"
+ ".o_command_category:nth-of-type(3) .o_command > a > div > span:first-child"
),
].map((el) => el.textContent),
["Command4"]
@@ -943,7 +943,7 @@ QUnit.test("don't display by categories if there is a search value", async (asse
assert.deepEqual(
[
...target.querySelectorAll(
- ".o_command_category:nth-of-type(1) .o_command > div > span:first-child"
+ ".o_command_category:nth-of-type(1) .o_command > a > div > span:first-child"
),
].map((el) => el.textContent),
["Command1", "Command2", "Command3"]
@@ -1547,3 +1547,51 @@ QUnit.test("generate new session id when opened", async (assert) => {
await nextTick();
assert.equal(lastSessionId, 1);
});
+
+QUnit.test("checks that href is correctly used", async (assert) => {
+ await mount(TestComponent, target, { env });
+ const providers = [
+ {
+ namespace: "@",
+ provide: () => [
+ {
+ name: "Command with link",
+ action: () => {
+ assert.step("command_with_link_clicked");
+ },
+ href: "https://www.odoo.com",
+ },
+ {
+ name: "Command without link",
+ action: () => {},
+ },
+ ],
+ },
+ ];
+ const config = {providers};
+ env.services.dialog.add(CommandPalette, {
+ config,
+ });
+ patchWithCleanup(window, {
+ open: (href) => {
+ assert.step(href.toString());
+ }
+ });
+ await nextTick();
+ await editSearchBar("@");
+ const command = target.querySelector(".o_command_palette .o_command");
+ // Check that command has link inside it
+ assert.strictEqual(command.querySelector("a").getAttribute("href"), "https://www.odoo.com");
+ // Check that we get url when doing ctrl+enter on a command having a link inside it
+ triggerHotkey("control+enter");
+ await nextTick();
+ assert.verifySteps(["https://www.odoo.com"]);
+ // Check that command has no link inside it
+ const commandWithoutLink = target.querySelector(".o_command_palette .o_command:nth-child(2)");
+ assert.strictEqual(commandWithoutLink.querySelector("a").getAttribute("href"), null);
+ // Check that clicking on a command having a link inside it triggers the command action
+ // instead of redirecting to the href (last step because it closes the command palette).
+ await click(command);
+ await nextTick();
+ assert.verifySteps(["command_with_link_clicked"]);
+});
diff --git a/addons/web/static/tests/core/commands/command_service_tests.js b/addons/web/static/tests/core/commands/command_service_tests.js
index c937db4a69d..2c357f50fc4 100644
--- a/addons/web/static/tests/core/commands/command_service_tests.js
+++ b/addons/web/static/tests/core/commands/command_service_tests.js
@@ -913,7 +913,7 @@ QUnit.test("data-command-category", async (assert) => {
assert.deepEqual(
[
...target.querySelectorAll(
- ".o_command_category:nth-of-type(1) .o_command > div > span:first-child"
+ ".o_command_category:nth-of-type(1) .o_command > a > div > span:first-child"
),
].map((el) => el.textContent),
["Robert baratheon", "Joffrey baratheon"]
@@ -921,7 +921,7 @@ QUnit.test("data-command-category", async (assert) => {
assert.deepEqual(
[
...target.querySelectorAll(
- ".o_command_category:nth-of-type(2) .o_command > div > span:first-child"
+ ".o_command_category:nth-of-type(2) .o_command > a > div > span:first-child"
),
].map((el) => el.textContent),
["Aria stark", "Bran stark"]
diff --git a/addons/web/static/tests/core/commands/menu_provider_tests.js b/addons/web/static/tests/core/commands/menu_provider_tests.js
index 6d0a99cf7a1..b57381964ec 100644
--- a/addons/web/static/tests/core/commands/menu_provider_tests.js
+++ b/addons/web/static/tests/core/commands/menu_provider_tests.js
@@ -80,7 +80,7 @@ QUnit.test("displays only apps if the search value is '/'", async (assert) => {
assert.containsOnce(target, ".o_command_category");
assert.containsN(target, ".o_command", 2);
assert.deepEqual(
- [...target.querySelectorAll(".o_command")].map((el) => el.textContent),
+ [...target.querySelectorAll(".o_command_name")].map((el) => el.textContent),
["Contact", "Sales"]
);
});
@@ -94,7 +94,7 @@ QUnit.test("displays apps and menu items if the search value is not only '/'", a
assert.containsOnce(target, ".o_command_palette");
assert.containsN(target, ".o_command", 3);
assert.deepEqual(
- [...target.querySelectorAll(".o_command")].map((el) => el.textContent),
+ [...target.querySelectorAll(".o_command_name")].map((el) => el.textContent),
["Sales", "Sales / Info", "Sales / Report"]
);
});