diff --git a/addons/web/static/src/core/debug/debug_menu.js b/addons/web/static/src/core/debug/debug_menu.js index 409dd5a603a..fad7c8562de 100644 --- a/addons/web/static/src/core/debug/debug_menu.js +++ b/addons/web/static/src/core/debug/debug_menu.js @@ -7,54 +7,60 @@ import { registry } from "../registry"; const debugRegistry = registry.category("debug"); const { Component } = owl; +let debugElementsId = 1; -export class DebugManager extends Component { +export class DebugMenu extends Component { setup() { - this.debugFactories = {}; + const globalContext = { env: this.env }; + this.activeCategories = new Set(); + this.contexts = new Map([["global", globalContext]]); this.debugService = useService("debug"); + this.hasAccessRights = false; // Defined as arrow to be passed as prop // @ts-ignore this.beforeOpenDropdown = async () => { - if (!this.accessRights) { - this.accessRights = await this.debugService.getAccessRights(); + if (!this.hasAccessRights) { + this.hasAccessRights = true; + const accessRights = await this.debugService.getAccessRights(); + Object.assign(globalContext, { accessRights }); } }; - this.env.bus.on("DEBUG-MANAGER:NEW-ITEMS", this, (payload) => { - const { inDialog, elementsId, elementsFactory } = payload; - if (this.isInDialog === inDialog) { - this.debugFactories[elementsId] = elementsFactory; + this.env.bus.on("DEBUG-MANAGER:ADD-CONTEXT", this, (payload) => { + const { category, context, inDialog, itemId } = payload; + if (this.env.inDialog === inDialog) { + this.contexts.set(itemId, context); + this.activeCategories.add(category); } }); - this.env.bus.on("DEBUG-MANAGER:REMOVE-ITEMS", this, (payload) => { - const { inDialog, elementsId } = payload; - if (this.isInDialog === inDialog) { - delete this.debugFactories[elementsId]; + this.env.bus.on("DEBUG-MANAGER:REMOVE-CONTEXT", this, (payload) => { + const { category, inDialog, itemId } = payload; + if (this.env.inDialog === inDialog) { + this.contexts.delete(itemId); + this.activeCategories.delete(category); } }); - if (!this.isInDialog) { - this.debugFactories.global = () => - debugRegistry.getAll().map((elFactory) => elFactory(this.env)); - } - } - - get isInDialog() { - return this.env.inDialog; } getElements() { - if (Object.keys(this.debugFactories).length > 0) { - const sortedElements = Object.values(this.debugFactories) - .map((factory) => factory(this.accessRights)) - .reduce((acc, elements) => acc.concat(elements)) - .sort((x, y) => { - const xSeq = x.sequence ? x.sequence : 1000; - const ySeq = y.sequence ? y.sequence : 1000; - return xSeq - ySeq; - }); - return sortedElements; - } else { - return []; + const factories = []; + const context = Object.assign({}, ...this.contexts.values()); + // If not in dialog => gets root (generic) factories + if (!this.env.inDialog) { + factories.push(...debugRegistry.getAll()); } + // Retrieves the active categories factories + for (const category of this.activeCategories) { + factories.push(...debugRegistry.category(category).getAll()); + } + // Builds, filters and sorts all items + return factories + .map((factory) => factory(context)) + .filter(Boolean) + .sort((x, y) => { + const xSeq = x.sequence ? x.sequence : 1000; + const ySeq = y.sequence ? y.sequence : 1000; + return xSeq - ySeq; + }); } onDropdownItemSelected(ev) { @@ -68,20 +74,22 @@ export class DebugManager extends Component { } } -DebugManager.debugElementsId = 1; -DebugManager.template = "web.DebugManager"; +DebugMenu.template = "web.DebugMenu"; -export function useDebugManager(elementsFactory) { - const elementsId = DebugManager.debugElementsId++; +export function useDebugMenu(category, context = {}) { const component = Component.current; const env = component.env; const payload = { - elementsId, - elementsFactory, + category, + context, inDialog: env.inDialog, + itemId: debugElementsId++, }; - useEffect(() => { - env.bus.trigger("DEBUG-MANAGER:NEW-ITEMS", payload); - return () => env.bus.trigger("DEBUG-MANAGER:REMOVE-ITEMS", payload); - }, () => []); + useEffect( + () => { + env.bus.trigger("DEBUG-MANAGER:ADD-CONTEXT", payload); + return () => env.bus.trigger("DEBUG-MANAGER:REMOVE-CONTEXT", payload); + }, + () => [] + ); } diff --git a/addons/web/static/src/core/debug/debug_menu.xml b/addons/web/static/src/core/debug/debug_menu.xml index dfbd68405b9..859cd877d83 100644 --- a/addons/web/static/src/core/debug/debug_menu.xml +++ b/addons/web/static/src/core/debug/debug_menu.xml @@ -1,7 +1,7 @@ - + - + - +
@@ -62,7 +62,7 @@
- + diff --git a/addons/web/static/src/core/debug/debug_service.js b/addons/web/static/src/core/debug/debug_service.js index e904f9f7889..8717e78da2b 100644 --- a/addons/web/static/src/core/debug/debug_service.js +++ b/addons/web/static/src/core/debug/debug_service.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { registry } from "../registry"; -import { DebugManager } from "./debug_menu"; +import { DebugMenu } from "./debug_menu"; export function editModelDebug(env, title, model, id) { return env.services.action.doAction({ @@ -21,9 +21,7 @@ export const debugService = { start(env, { orm }) { let accessRightsProm; if (env.debug !== "") { - registry - .category("systray") - .add("web.debug_mode_menu", DebugManager, { sequence: 100 }); + registry.category("systray").add("web.debug_mode_menu", DebugMenu, { sequence: 100 }); } return { diff --git a/addons/web/static/src/legacy/action_adapters.js b/addons/web/static/src/legacy/action_adapters.js index 2a9c6165a1d..e1b4d6fcb25 100644 --- a/addons/web/static/src/legacy/action_adapters.js +++ b/addons/web/static/src/legacy/action_adapters.js @@ -1,18 +1,16 @@ /** @odoo-module **/ -import core from "web.core"; import Context from "web.Context"; -import { useService } from "../core/service_hook"; -import { useEffect } from "../core/effect_hook"; -import { useBus } from "../core/bus_hook"; -import { ViewNotFoundError } from "../webclient/actions/action_service"; -import { useDebugManager } from "../core/debug/debug_menu"; -import { objectToQuery } from "../core/browser/router_service"; +import core from "web.core"; import { ComponentAdapter } from "web.OwlCompatibility"; -import { mapDoActionOptionAPI } from "./utils"; -import { setupDebugAction, setupDebugViewForm, setupDebugView } from "./debug_manager"; -import { cleanDomFromBootstrap } from "./utils"; +import { objectToQuery } from "../core/browser/router_service"; +import { useBus } from "../core/bus_hook"; +import { useDebugMenu } from "../core/debug/debug_menu"; import { Dialog } from "../core/dialog/dialog"; +import { useEffect } from "../core/effect_hook"; +import { useService } from "../core/service_hook"; +import { ViewNotFoundError } from "../webclient/actions/action_service"; +import { cleanDomFromBootstrap, mapDoActionOptionAPI } from "./utils"; const { Component, tags } = owl; @@ -154,9 +152,7 @@ class ActionAdapter extends ComponentAdapter { export class ClientActionAdapter extends ActionAdapter { setup() { super.setup(); - useDebugManager((accessRights) => - setupDebugAction(accessRights, this.wowlEnv, this.props.widgetArgs[0]) - ); + useDebugMenu("action", { action: this.props.widgetArgs[0] }); this.env = Component.env; } @@ -231,16 +227,13 @@ export class ViewAdapter extends ActionAdapter { this.shouldUpdateWidget = true; this.magicReload = useMagicLegacyReload(); const envWowl = this.env; - useDebugManager((accessRights) => - setupDebugAction(accessRights, envWowl, this.props.viewParams.action) - ); - useDebugManager((accessRights) => - setupDebugView(accessRights, envWowl, this, this.props.viewParams.action) - ); + useDebugMenu("action", { + action: this.props.viewParams.action, + component: this, + }); + useDebugMenu("view"); if (this.props.viewInfo.type === "form") { - useDebugManager((accessRights) => - setupDebugViewForm(envWowl, this, this.props.viewParams.action) - ); + useDebugMenu("form"); } if (!envWowl.inDialog) { useBus(envWowl.bus, "ACTION_MANAGER:UPDATE", (info) => { diff --git a/addons/web/static/src/legacy/debug_manager.js b/addons/web/static/src/legacy/debug_manager.js index bedb818bb80..ed909667243 100644 --- a/addons/web/static/src/legacy/debug_manager.js +++ b/addons/web/static/src/legacy/debug_manager.js @@ -1,34 +1,59 @@ /** @odoo-module **/ import { editModelDebug } from "../core/debug/debug_service"; -import { formatDateTime, parseDateTime } from "../core/l10n/dates"; -import { formatMany2one } from "../fields/format"; import { Dialog } from "../core/dialog/dialog"; +import { formatDateTime, parseDateTime } from "../core/l10n/dates"; import { _lt } from "../core/l10n/translation"; +import { registry } from "../core/registry"; +import { formatMany2one } from "../fields/format"; +import { json_node_to_xml } from "../views/view_utils"; const { hooks, tags } = owl; const { useState } = hooks; -export function setupDebugAction(accessRights, env, action) { - const actionSeparator = { +// Action items + +function actionSeparator() { + return { type: "separator", sequence: 100, }; +} - let description = env._t("Edit Action"); - const editAction = { +function accessSeparator({ accessRights, action }) { + const { canSeeModelAccess, canSeeRecordRules } = accessRights; + if (!action.res_model || (!canSeeModelAccess && !canSeeRecordRules)) { + return null; + } + return { + type: "separator", + sequence: 200, + }; +} + +function editAction({ action, env }) { + if (!action.id) { + return null; + } + const description = env._t("Edit Action"); + return { type: "item", - description: description, + description, callback: () => { editModelDebug(env, description, action.type, action.id); }, sequence: 110, }; +} - description = env._t("View Fields"); - const viewFields = { +function viewFields({ action, env }) { + if (!action.res_model) { + return null; + } + const description = env._t("View Fields"); + return { type: "item", - description: description, + description, callback: async () => { const modelId = ( await env.services.orm.search("ir.model", [["model", "=", action.res_model]], { @@ -51,11 +76,16 @@ export function setupDebugAction(accessRights, env, action) { }, sequence: 120, }; +} - description = env._t("Manage Filters"); - const manageFilters = { +function manageFilters({ action, env }) { + if (!action.res_model) { + return null; + } + const description = env._t("Manage Filters"); + return { type: "item", - description: description, + description, callback: () => { // manage_filters env.services.action.doAction({ @@ -74,8 +104,13 @@ export function setupDebugAction(accessRights, env, action) { }, sequence: 130, }; +} - const technicalTranslation = { +function technicalTranslation({ action, env }) { + if (!action.res_model) { + return null; + } + return { type: "item", description: env._t("Technical Translation"), callback: async () => { @@ -88,16 +123,16 @@ export function setupDebugAction(accessRights, env, action) { }, sequence: 140, }; +} - const accessSeparator = { - type: "separator", - sequence: 200, - }; - - description = env._t("View Access Rights"); - const viewAccessRights = { +function viewAccessRights({ accessRights, action, env }) { + if (!action.res_model || !accessRights.canSeeModelAccess) { + return null; + } + const description = env._t("View Access Rights"); + return { type: "item", - description: description, + description, callback: async () => { const modelId = ( await env.services.orm.search("ir.model", [["model", "=", action.res_model]], { @@ -120,9 +155,14 @@ export function setupDebugAction(accessRights, env, action) { }, sequence: 210, }; +} - description = env._t("Model Record Rules"); - const viewRecordRules = { +function viewRecordRules({ accessRights, action, env }) { + if (!action.res_model || !accessRights.canSeeRecordRules) { + return null; + } + const description = env._t("Model Record Rules"); + return { type: "item", description: env._t("View Record Rules"), callback: async () => { @@ -147,26 +187,6 @@ export function setupDebugAction(accessRights, env, action) { }, sequence: 220, }; - - const result = [actionSeparator]; - if (action.id) { - result.push(editAction); - } - if (action.res_model) { - result.push(viewFields); - result.push(manageFilters); - result.push(technicalTranslation); - if (accessRights.canSeeModelAccess || accessRights.canSeeRecordRules) { - result.push(accessSeparator); - if (accessRights.canSeeModelAccess) { - result.push(viewAccessRights); - } - if (accessRights.canSeeRecordRules) { - result.push(viewRecordRules); - } - } - } - return result; } class FieldViewGetDialog extends Dialog {} @@ -207,7 +227,7 @@ class GetMetadataDialog extends Dialog { this.state.write_date = formatDateTime(parseDateTime(metadata.write_date)); } } -GetMetadataDialog.bodyTemplate = "web.DebugManager.getMetadataBody"; +GetMetadataDialog.bodyTemplate = "web.DebugMenu.getMetadataBody"; GetMetadataDialog.title = _lt("View Metadata"); class SetDefaultDialog extends Dialog { @@ -351,17 +371,19 @@ class SetDefaultDialog extends Dialog { this.trigger("dialog-closed"); } } -SetDefaultDialog.bodyTemplate = "web.DebugManager.setDefaultBody"; -SetDefaultDialog.footerTemplate = "web.DebugManager.SetDefaultFooter"; +SetDefaultDialog.bodyTemplate = "web.DebugMenu.setDefaultBody"; +SetDefaultDialog.footerTemplate = "web.DebugMenu.SetDefaultFooter"; SetDefaultDialog.title = _lt("Set Default"); -export function setupDebugView(accessRights, env, component, action) { - const viewId = component.props.viewInfo.view_id; - const viewSeparator = { +function viewSeparator() { + return { type: "separator", sequence: 300, }; - const fieldsViewGet = { +} + +function fieldsViewGet({ component, env }) { + return { type: "item", description: env._t("Fields View Get"), callback: () => { @@ -372,57 +394,72 @@ export function setupDebugView(accessRights, env, component, action) { }, sequence: 340, }; +} + +export function editView({ accessRights, action, component, env }) { + if (!accessRights.canEditView) { + return null; + } + const { viewId, viewType } = component.widget; const displayName = action.views - .find((v) => v.type === component.widget.viewType) + .find((v) => v.type === viewType) .name.toString(); - let description = env._t("Edit View: ") + displayName; - const editView = { + const description = env._t("Edit View: ") + displayName; + return { type: "item", - description: description, + description, callback: () => { editModelDebug(env, description, "ir.ui.view", viewId); }, sequence: 350, }; - description = env._t("Edit ControlPanelView"); - const editControlPanelView = { +} + +function editControlPanelView({ accessRights, component, env }) { + if (!accessRights.canEditView) { + return null; + } + const description = env._t("Edit ControlPanelView"); + return { type: "item", - description: description, + description, callback: () => { editModelDebug( env, description, "ir.ui.view", - component.props.viewParams.action.controlPanelFieldsView.view_id + component.props.viewInfo.view_id ); }, sequence: 360, }; - const result = [viewSeparator, fieldsViewGet]; - if (accessRights.canEditView) { - result.push(editView); - result.push(editControlPanelView); - } - return result; } -export function setupDebugViewForm(env, component, action) { - const setDefaults = { +// Form view itemss + +function setDefaults({ action, component, env }) { + return { type: "item", description: env._t("Set Defaults"), callback: () => { env.services.dialog.open(SetDefaultDialog, { res_model: action.res_model, - component: component, + component, }); }, sequence: 310, }; - const viewMetadata = { +} + +function viewMetadata({ action, component, env }) { + const selectedIds = component.widget.getSelectedIds(); + if (selectedIds.length !== 1) { + return null; + } + return { type: "item", description: env._t("View Metadata"), callback: () => { - const selectedIds = component.widget.getSelectedIds(); env.services.dialog.open(GetMetadataDialog, { res_model: action.res_model, selectedIds, @@ -430,12 +467,19 @@ export function setupDebugViewForm(env, component, action) { }, sequence: 320, }; +} + +function manageAttachments({ action, component, env }) { + const selectedIds = component.widget.getSelectedIds(); const description = env._t("Manage Attachments"); - const manageAttachments = { + if (selectedIds.length !== 1) { + return null; + } + return { type: "item", - description: description, + description, callback: () => { - const selectedId = component.widget.getSelectedIds()[0]; + const selectedId = selectedIds[0]; env.services.action.doAction({ res_model: "ir.attachment", name: description, @@ -456,10 +500,30 @@ export function setupDebugViewForm(env, component, action) { }, sequence: 330, }; - const result = [setDefaults]; - if (component.widget.getSelectedIds().length === 1) { - result.push(viewMetadata); - result.push(manageAttachments); - } - return result; } + +const debugRegistry = registry.category("debug"); + +debugRegistry + .category("action") + .add("actionSeparator", actionSeparator) + .add("editAction", editAction) + .add("viewFields", viewFields) + .add("manageFilters", manageFilters) + .add("technicalTranslation", technicalTranslation) + .add("accessSeparator", accessSeparator) + .add("viewAccessRights", viewAccessRights) + .add("viewRecordRules", viewRecordRules) + +debugRegistry + .category("view") + .add("viewSeparator", viewSeparator) + .add("fieldsViewGet", fieldsViewGet) + .add("editView", editView) + .add("editControlPanelView", editControlPanelView) + +debugRegistry + .category("form") + .add("setDefaults", setDefaults) + .add("viewMetadata", viewMetadata) + .add("manageAttachments", manageAttachments); diff --git a/addons/web/static/src/webclient/actions/action_dialog.js b/addons/web/static/src/webclient/actions/action_dialog.js index de8a30c6abe..2938f69cd6f 100644 --- a/addons/web/static/src/webclient/actions/action_dialog.js +++ b/addons/web/static/src/webclient/actions/action_dialog.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { Dialog } from "../../core/dialog/dialog"; -import { DebugManager } from "../../core/debug/debug_menu"; +import { DebugMenu } from "../../core/debug/debug_menu"; import { useEffect } from "../../core/effect_hook"; const { hooks } = owl; @@ -25,7 +25,7 @@ class ActionDialog extends Dialog { this.title = "title" in this.props ? this.props.title : this.constructor.title; } } -ActionDialog.components = { ...Dialog.components, DebugManager }; +ActionDialog.components = { ...Dialog.components, DebugMenu }; ActionDialog.template = "web.ActionDialog"; ActionDialog.bodyTemplate = "web.ActionDialogBody"; ActionDialog.props = { @@ -52,18 +52,21 @@ class LegacyAdaptedActionDialog extends ActionDialog { const ControllerComponent = this.props && this.props.ActionComponent; const Controller = ControllerComponent && ControllerComponent.Component; this.isLegacy = Controller && Controller.isLegacy; - useEffect(() => { - if (this.isLegacy) { - // Retrieve the widget climbing the wrappers - const componentController = this.actionRef.comp; - const controller = componentController.componentRef.comp; - const viewAdapter = controller.controllerRef.comp; - const widget = viewAdapter.widget; - // Render legacy footer buttons - const footer = this.modalRef.el.querySelector("footer"); - widget.renderButtons($(footer)); - } - }, () => []); // TODO: should this depend on actionRef.comp? + useEffect( + () => { + if (this.isLegacy) { + // Retrieve the widget climbing the wrappers + const componentController = this.actionRef.comp; + const controller = componentController.componentRef.comp; + const viewAdapter = controller.controllerRef.comp; + const widget = viewAdapter.widget; + // Render legacy footer buttons + const footer = this.modalRef.el.querySelector("footer"); + widget.renderButtons($(footer)); + } + }, + () => [] + ); // TODO: should this depend on actionRef.comp? } } LegacyAdaptedActionDialog.footerTemplate = "web.LegacyAdaptedActionDialogFooter"; diff --git a/addons/web/static/src/webclient/actions/action_dialog.xml b/addons/web/static/src/webclient/actions/action_dialog.xml index b5baba72b1c..d944772cc57 100644 --- a/addons/web/static/src/webclient/actions/action_dialog.xml +++ b/addons/web/static/src/webclient/actions/action_dialog.xml @@ -3,7 +3,7 @@ - + diff --git a/addons/web/static/src/webclient/clickbot/clickbot_loader.js b/addons/web/static/src/webclient/clickbot/clickbot_loader.js index 497450f13e1..3096f397e39 100644 --- a/addons/web/static/src/webclient/clickbot/clickbot_loader.js +++ b/addons/web/static/src/webclient/clickbot/clickbot_loader.js @@ -9,7 +9,7 @@ export default async function startClickEverywhere(xmlId, appsMenusOnly) { window.clickEverywhere(xmlId, appsMenusOnly); } -function runClickTestItem(env) { +function runClickTestItem({ env }) { return { type: "item", description: env._t("Run Click Everywhere Test"), diff --git a/addons/web/static/tests/core/debug/debug_manager_tests.js b/addons/web/static/tests/core/debug/debug_manager_tests.js index 2fe1ba3885d..4bbeea81a75 100644 --- a/addons/web/static/tests/core/debug/debug_manager_tests.js +++ b/addons/web/static/tests/core/debug/debug_manager_tests.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import { browser } from "@web/core/browser/browser"; -import { DebugManager, useDebugManager } from "@web/core/debug/debug_menu"; +import { DebugMenu, useDebugMenu } from "@web/core/debug/debug_menu"; import { regenerateAssets } from "@web/core/debug/debug_menu_items"; import { registry } from "@web/core/registry"; import { debugService } from "@web/core/debug/debug_service"; @@ -21,7 +21,7 @@ const debugRegistry = registry.category("debug"); let target; let testConfig; -QUnit.module("DebugManager", (hooks) => { +QUnit.module("DebugMenu", (hooks) => { hooks.beforeEach(async () => { target = getFixture(); registry @@ -38,61 +38,62 @@ QUnit.module("DebugManager", (hooks) => { testConfig = { mockRPC }; }); QUnit.test("can be rendered", async (assert) => { - debugRegistry.add("item_1", () => { - return { - type: "item", - description: "Item 1", - callback: () => { - assert.step("callback item_1"); - }, - sequence: 10, - }; - }); - debugRegistry.add("item_2", () => { - return { - type: "item", - description: "Item 2", - callback: () => { - assert.step("callback item_2"); - }, - sequence: 5, - }; - }); - debugRegistry.add("item_3", () => { - return { - type: "item", - description: "Item 3", - callback: () => { - assert.step("callback item_3"); - }, - }; - }); - debugRegistry.add("separator", () => { - return { - type: "separator", - sequence: 20, - }; - }); - debugRegistry.add("separator_2", () => { - return { - type: "separator", - sequence: 7, - hide: true, - }; - }); - debugRegistry.add("item_4", () => { - return { - type: "item", - description: "Item 4", - callback: () => { - assert.step("callback item_4"); - }, - hide: true, - sequence: 10, - }; - }); + debugRegistry + .add("item_1", () => { + return { + type: "item", + description: "Item 1", + callback: () => { + assert.step("callback item_1"); + }, + sequence: 10, + }; + }) + .add("item_2", () => { + return { + type: "item", + description: "Item 2", + callback: () => { + assert.step("callback item_2"); + }, + sequence: 5, + }; + }) + .add("item_3", () => { + return { + type: "item", + description: "Item 3", + callback: () => { + assert.step("callback item_3"); + }, + }; + }) + .add("separator", () => { + return { + type: "separator", + sequence: 20, + }; + }) + .add("separator_2", () => { + return { + type: "separator", + sequence: 7, + hide: true, + }; + }) + .add("item_4", () => { + return { + type: "item", + description: "Item 4", + callback: () => { + assert.step("callback item_4"); + }, + hide: true, + sequence: 10, + }; + }); const env = await makeTestEnv(testConfig); - const debugManager = await mount(DebugManager, { env, target }); + const debugManager = await mount(DebugMenu, { env, target }); registerCleanup(() => debugManager.destroy()); let debugManagerEl = debugManager.el; await click(debugManager.el.querySelector("button.o_dropdown_toggler")); @@ -117,7 +118,7 @@ QUnit.module("DebugManager", (hooks) => { assert.verifySteps(["callback item_2", "callback item_1", "callback item_3"]); }); - QUnit.test("Don't display the DebugManager if debug mode is disabled", async (assert) => { + QUnit.test("Don't display the DebugMenu if debug mode is disabled", async (assert) => { const dialogContainer = document.createElement("div"); dialogContainer.classList.add("o_dialog_container"); target.append(dialogContainer); @@ -132,8 +133,9 @@ QUnit.module("DebugManager", (hooks) => { }); QUnit.test( - "Display the DebugManager correctly in a ActionDialog if debug mode is enabled", + "Display the DebugMenu correctly in a ActionDialog if debug mode is enabled", async (assert) => { + assert.expect(8); const dialogContainer = document.createElement("div"); dialogContainer.classList.add("o_dialog_container"); target.append(dialogContainer); @@ -147,26 +149,33 @@ QUnit.module("DebugManager", (hooks) => { sequence: 0, }; }); - const item1 = { - type: "item", - description: "Item 1", - callback: () => { - assert.step("callback item_1"); - }, - sequence: 10, - }; - const item2 = { - type: "item", - description: "Item 2", - callback: () => { - assert.step("callback item_2"); - }, - sequence: 20, - }; + debugRegistry + .category("custom") + .add("item1", () => { + return { + type: "item", + description: "Item 1", + callback: () => { + assert.step("callback item_1"); + }, + sequence: 10, + }; + }) + .add("item2", ({ customKey }) => { + return { + type: "item", + description: "Item 2", + callback: () => { + assert.step("callback item_2"); + assert.strictEqual(customKey, "abc"); + }, + sequence: 20, + }; + }); class Parent extends Component { setup() { useSubEnv({ inDialog: true }); - useDebugManager(() => [item1, item2]); + useDebugMenu("custom", { customKey: "abc" }); } } Parent.components = { ActionDialog }; @@ -224,7 +233,7 @@ QUnit.module("DebugManager", (hooks) => { registry.category("services").add("localization", makeFakeLocalizationService()); debugRegistry.add("regenerateAssets", regenerateAssets); const env = await makeTestEnv(testConfig); - const debugManager = await mount(DebugManager, { env, target }); + const debugManager = await mount(DebugMenu, { env, target }); registerCleanup(() => debugManager.destroy()); await click(debugManager.el.querySelector("button.o_dropdown_toggler")); assert.containsOnce(debugManager.el, "ul.o_dropdown_menu li.o_dropdown_item"); diff --git a/addons/web/static/tests/helpers/mock_env.js b/addons/web/static/tests/helpers/mock_env.js index 18a3866e049..60e6ee00d9c 100644 --- a/addons/web/static/tests/helpers/mock_env.js +++ b/addons/web/static/tests/helpers/mock_env.js @@ -13,6 +13,7 @@ export function clearRegistryWithCleanup(registry) { content: {}, elements: null, entries: null, + subRegistries: {}, // Preserve OnUpdate handlers subscriptions: { UPDATE: [...registry.subscriptions.UPDATE] }, }; @@ -24,6 +25,7 @@ function cloneRegistryWithCleanup(registry) { content: { ...registry.content }, elements: null, entries: null, + subRegistries: {}, // Preserve OnUpdate handlers subscriptions: { UPDATE: [...registry.subscriptions.UPDATE] }, }; diff --git a/addons/web/static/tests/webclient/actions/legacy_tests.js b/addons/web/static/tests/webclient/actions/legacy_tests.js index 9095994922d..7c3794ba6ef 100644 --- a/addons/web/static/tests/webclient/actions/legacy_tests.js +++ b/addons/web/static/tests/webclient/actions/legacy_tests.js @@ -10,7 +10,7 @@ import { clearRegistryWithCleanup } from "../../helpers/mock_env"; import { createWebClient, doAction, getActionManagerTestConfig } from "./helpers"; import { ClientActionAdapter } from "@web/legacy/action_adapters"; -import { useDebugManager } from "@web/core/debug/debug_menu"; +import { useDebugMenu } from "@web/core/debug/debug_menu"; import { debugService } from "@web/core/debug/debug_service"; import ControlPanel from "web.ControlPanel"; @@ -101,19 +101,23 @@ QUnit.module("ActionManager", (hooks) => { patchWithCleanup(ClientActionAdapter.prototype, { setup() { - useDebugManager((accessRights) => { - assert.step("debugItems executed"); - assert.ok(this.widget); - return []; - }); + useDebugMenu("custom", { widget: this }); this._super(); }, }); + registry + .category("debug") + .category("custom") + .add("item1", ({ widget }) => { + assert.step("debugItems executed"); + assert.ok(widget); + return {}; + }); registry.category("services").add("debug", debugService); testConfig.debug = true; - const mockRPC = (route, args) => { + const mockRPC = (route) => { if (route.includes("check_access_rights")) { return true; } diff --git a/addons/web/static/tests/webclient/actions/window_action_tests.js b/addons/web/static/tests/webclient/actions/window_action_tests.js index 12d2af5e6b2..cd0e749b391 100644 --- a/addons/web/static/tests/webclient/actions/window_action_tests.js +++ b/addons/web/static/tests/webclient/actions/window_action_tests.js @@ -3,12 +3,13 @@ import { debugService } from "@web/core/debug/debug_service"; import { DialogContainer } from "@web/core/dialog/dialog_service"; import { registry } from "@web/core/registry"; +import { editView } from "@web/legacy/debug_manager"; import { clearUncommittedChanges } from "@web/webclient/actions/action_service"; -import testUtils from "web.test_utils"; -import ListController from "web.ListController"; import AbstractView from "web.AbstractView"; -import legacyViewRegistry from "web.view_registry"; import FormView from "web.FormView"; +import ListController from "web.ListController"; +import testUtils from "web.test_utils"; +import legacyViewRegistry from "web.view_registry"; import { clearRegistryWithCleanup } from "../../helpers/mock_env"; import { makeFakeUserService } from "../../helpers/mock_services"; import { click, legacyExtraNextTick, nextTick } from "../../helpers/utils"; @@ -2227,8 +2228,9 @@ QUnit.module("ActionManager", (hooks) => { assert.expect(2); serviceRegistry.add("debug", debugService); + registry.category("debug").category("view").add("editView", editView); testConfig.debug = "1"; - const mockRPC = async (route, args) => { + const mockRPC = async (route) => { if (route.includes("check_access_rights")) { return true; } diff --git a/addons/web_tour/static/src/debug/debug_manager.js b/addons/web_tour/static/src/debug/debug_manager.js index ed2580bd131..c50a20ca442 100644 --- a/addons/web_tour/static/src/debug/debug_manager.js +++ b/addons/web_tour/static/src/debug/debug_manager.js @@ -5,13 +5,13 @@ import { registry } from "@web/core/registry"; import ToursDialog from "@web_tour/debug/tour_dialog_component"; import utils from "web_tour.utils"; -function debugDisableTourItem(env) { +function disableTours({ env }) { if (!env.services.user.isSystem) { - return {}; + return null; } const activeTours = env.services.tour.getActiveTours(); if (activeTours.length === 0) { - return {}; + return null; } return { type: "item", @@ -23,13 +23,13 @@ function debugDisableTourItem(env) { } browser.location.reload(); }, - sequence: 33, + sequence: 50, }; } -function debugStartTourItem(env) { +function startTour({ env }) { if (!env.services.user.isSystem) { - return {}; + return null; } return { type: "item", @@ -37,11 +37,11 @@ function debugStartTourItem(env) { callback: async () => { env.services.dialog.open(ToursDialog); }, - sequence: 32, + sequence: 60, }; } registry .category("debug") - .add("web_tour.start_tour", debugStartTourItem) - .add("web_tour.disable_tour", debugDisableTourItem); + .add("web_tour.startTour", startTour) + .add("web_tour.disableTours", disableTours);
ID: