diff --git a/addons/account/static/tests/tours/account_dashboard_setup_bar_tests.js b/addons/account/static/tests/tours/account_dashboard_setup_bar_tests.js index 0d11b027163..cdbe6c75339 100644 --- a/addons/account/static/tests/tours/account_dashboard_setup_bar_tests.js +++ b/addons/account/static/tests/tours/account_dashboard_setup_bar_tests.js @@ -20,7 +20,7 @@ odoo.define('account.dashboard.setup.tour', function (require) { }, { trigger: '.o_control_panel button:contains("' + _t('Print') + '")', }, { - trigger: '.o_control_panel .o_dropdown_menu a:contains("' + _t('Invoices without Payment') + '")', + trigger: '.o_control_panel .dropdown-menu a:contains("' + _t('Invoices without Payment') + '")', }, { trigger: 'iframe .o_report_layout_standard h2', content: 'Primary color is correct', diff --git a/addons/auth_totp/static/tests/totp_flow.js b/addons/auth_totp/static/tests/totp_flow.js index 868be3db6a0..6f2f29ba7eb 100644 --- a/addons/auth_totp/static/tests/totp_flow.js +++ b/addons/auth_totp/static/tests/totp_flow.js @@ -161,7 +161,7 @@ tour.register('totp_login_device', { run: 'click', }, { content: "click the Log out button", - trigger: 'li[data-menu=logout] > a', + trigger: '.dropdown-item[data-menu=logout]', }, { content: "check that we're back on the login page or go to it", trigger: 'input#login, a:contains(Log in)' @@ -280,7 +280,7 @@ tour.register('totp_admin_disables', { } }, { content: "Open Actions menu", - trigger: 'button.o_dropdown_toggler:contains("Action")' + trigger: 'button.dropdown-toggle:contains("Action")' }, { content: "Select totp remover", trigger: 'a.dropdown-item:contains(Disable two-factor authentication)' diff --git a/addons/board/static/src/add_to_board/add_to_board.xml b/addons/board/static/src/add_to_board/add_to_board.xml index 6419bd154af..98fdb8aad9b 100644 --- a/addons/board/static/src/add_to_board/add_to_board.xml +++ b/addons/board/static/src/add_to_board/add_to_board.xml @@ -4,12 +4,12 @@ Add to my dashboard -
  • +
    -
  • -
  • + +
    -
  • +
    diff --git a/addons/board/static/tests/dashboard_tests.js b/addons/board/static/tests/dashboard_tests.js index 7d2129d8a7f..8b05c697b55 100644 --- a/addons/board/static/tests/dashboard_tests.js +++ b/addons/board/static/tests/dashboard_tests.js @@ -7,6 +7,8 @@ odoo.define("board.dashboard_tests", function (require) { var testUtils = require("web.test_utils"); var ListRenderer = require("web.ListRenderer"); var pyUtils = require("web.py_utils"); + const { browser } = require("@web/core/browser/browser"); + const { patchWithCleanup } = require("@web/../tests/helpers/utils"); const { registry } = require("@web/core/registry"); const { makeFakeUserService } = require("@web/../tests/helpers/mock_services"); const { @@ -844,6 +846,7 @@ odoo.define("board.dashboard_tests", function (require) { "partner,false,list": '', "partner,false,search": "", }; + patchWithCleanup(browser, { setTimeout: (fn) => fn() }); const mockRPC = (route, args) => { if (route === "/board/add_to_dashboard") { @@ -895,9 +898,9 @@ odoo.define("board.dashboard_tests", function (require) { // add this action to dashboard await toggleFavoriteMenu(webClient); - await testUtils.dom.click($(".o_add_to_board button.o_dropdown_toggler")); + await testUtils.dom.triggerEvent($(".o_add_to_board button.dropdown-toggle"), "mouseenter"); await testUtils.fields.editInput($(".o_add_to_board input"), "a name"); - await testUtils.dom.click($(".o_add_to_board .o_dropdown_menu button")); + await testUtils.dom.click($(".o_add_to_board .dropdown-menu button")); testUtils.mock.unpatch(ListController); }); @@ -906,6 +909,7 @@ odoo.define("board.dashboard_tests", function (require) { // the second search saved should not be influenced by the first assert.expect(2); + patchWithCleanup(browser, { setTimeout: (fn) => fn() }); serverData.views = { "partner,false,list": '', "partner,false,search": "", @@ -951,8 +955,8 @@ odoo.define("board.dashboard_tests", function (require) { // Add it to dashboard await toggleFavoriteMenu(webClient); - await testUtils.dom.click($(".o_add_to_board button.o_dropdown_toggler")); - await testUtils.dom.click($(".o_add_to_board .o_dropdown_menu button")); + await testUtils.dom.triggerEvent($(".o_add_to_board button.dropdown-toggle"), "mouseenter"); + await testUtils.dom.click($(".o_add_to_board .dropdown-menu button")); // Remove it await testUtils.dom.click(webClient.el.querySelector(".o_facet_remove")); @@ -964,11 +968,12 @@ odoo.define("board.dashboard_tests", function (require) { await applyFilter(webClient); // Add it to dashboard await toggleFavoriteMenu(webClient); - await testUtils.dom.click( - webClient.el.querySelector(".o_add_to_board button.o_dropdown_toggler") + await testUtils.dom.triggerEvent( + webClient.el.querySelector(".o_add_to_board button.dropdown-toggle"), + "mouseenter" ); await testUtils.dom.click( - webClient.el.querySelector(".o_add_to_board .o_dropdown_menu button") + webClient.el.querySelector(".o_add_to_board .dropdown-menu button") ); }); @@ -976,6 +981,7 @@ odoo.define("board.dashboard_tests", function (require) { // View domains are to be added to the dashboard domain assert.expect(1); + patchWithCleanup(browser, { setTimeout: (fn) => fn() }); var view_domain = ["display_name", "ilike", "a"]; var filter_domain = ["display_name", "ilike", "b"]; @@ -1011,12 +1017,13 @@ odoo.define("board.dashboard_tests", function (require) { await applyFilter(webClient); // Add it to dashboard await toggleFavoriteMenu(webClient); - await testUtils.dom.click( - webClient.el.querySelector(".o_add_to_board button.o_dropdown_toggler") + await testUtils.dom.triggerEvent( + webClient.el.querySelector(".o_add_to_board button.dropdown-toggle"), + "mouseenter" ); // add await testUtils.dom.click( - webClient.el.querySelector(".o_add_to_board .o_dropdown_menu button") + webClient.el.querySelector(".o_add_to_board .dropdown-menu button") ); }); @@ -1255,7 +1262,7 @@ odoo.define("board.dashboard_tests", function (require) { // add the view to the dashboard await toggleFavoriteMenu(webClient); - await testUtils.dom.click($(".o_add_to_board button.o_dropdown_toggler")); + await testUtils.dom.click($(".o_add_to_board button.dropdown-toggle")); await testUtils.fields.editInput($(".o_add_to_board input"), "a name"); await testUtils.dom.click($(".o_add_to_board div button")); diff --git a/addons/crm/static/tests/forecast_view_tests.js b/addons/crm/static/tests/forecast_view_tests.js index 7d2992142e7..030adc9c560 100644 --- a/addons/crm/static/tests/forecast_view_tests.js +++ b/addons/crm/static/tests/forecast_view_tests.js @@ -7,7 +7,7 @@ import { toggleFilterMenu, toggleGroupByMenu, toggleMenuItem, - toggleMenuItemOption + toggleMenuItemOption, } from "@web/../tests/search/helpers"; import { makeView } from "@web/../tests/views/helpers"; import { createWebClient, doAction } from "@web/../tests/webclient/helpers"; @@ -18,6 +18,7 @@ import AbstractModel from "web.AbstractModel"; import AbstractView from "web.AbstractView"; import { controlPanel as cpHelpers, mock } from "web.test_utils"; import legacyViewRegistry from "web.view_registry"; +import { browser } from "@web/core/browser/browser"; const patchDate = mock.patchDate; @@ -72,6 +73,7 @@ QUnit.module("Views", (hooks) => { QUnit.test("Forecast graph view", async function (assert) { assert.expect(5); + patchWithCleanup(browser, { setTimeout: (fn) => fn() }); const unpatchDate = patchDate(2021, 8, 16, 16, 54, 0); const expectedDomains = [ @@ -167,6 +169,7 @@ QUnit.module("Views", (hooks) => { async function (assert) { assert.expect(16); + patchWithCleanup(browser, { setTimeout: (fn) => fn() }); const unpatchDate = patchDate(2021, 8, 16, 16, 54, 0); const expectedDomains = [ diff --git a/addons/crm/static/tests/tours/crm_forecast_tour.js b/addons/crm/static/tests/tours/crm_forecast_tour.js index df08fea7171..730c9c82ac0 100644 --- a/addons/crm/static/tests/tours/crm_forecast_tour.js +++ b/addons/crm/static/tests/tours/crm_forecast_tour.js @@ -11,11 +11,11 @@ tour.register('crm_forecast', { trigger: ".o_app[data-menu-xmlid='crm.crm_menu_root']", content: "open crm app", }, { - trigger: '.o_dropdown_toggler[data-menu-xmlid="crm.crm_menu_report"]', + trigger: '.dropdown-toggle[data-menu-xmlid="crm.crm_menu_report"]', content: 'Open Reporting menu', run: 'click', }, { - trigger: '.o_dropdown_item[data-menu-xmlid="crm.crm_menu_forecast"]', + trigger: '.dropdown-item[data-menu-xmlid="crm.crm_menu_forecast"]', content: 'Open Forecast menu', run: 'click', }, { diff --git a/addons/hr_expense/static/src/js/tours/hr_expense.js b/addons/hr_expense/static/src/js/tours/hr_expense.js index b8200677c51..a2dffe7f4f0 100644 --- a/addons/hr_expense/static/src/js/tours/hr_expense.js +++ b/addons/hr_expense/static/src/js/tours/hr_expense.js @@ -35,7 +35,7 @@ tour.register('hr_expense_tour' , { content: Markup(_t('

    Select expenses to submit them to your manager

    ')), position: 'bottom' }, { - trigger: '.o_dropdown_toggler', + trigger: '.dropdown-toggle', extra_trigger: ".o_expense_tree", content: Markup(_t('

    Click on Action Create Report to submit selected expenses to your manager

    ')), position: 'right', diff --git a/addons/mail/static/src/components/messaging_menu/messaging_menu.scss b/addons/mail/static/src/components/messaging_menu/messaging_menu.scss index ad9e3a144a3..3d48947cad9 100644 --- a/addons/mail/static/src/components/messaging_menu/messaging_menu.scss +++ b/addons/mail/static/src/components/messaging_menu/messaging_menu.scss @@ -107,7 +107,7 @@ // ------------------------------------------------------------------ // Make hightlight more consistent, due to messaging menu looking quite similar to discuss app in mobile -.o_MessagingMenu.o-is-open { +.o_MessagingMenu.show { background-color: rgba($black, 0.1); } diff --git a/addons/mail/static/src/components/messaging_menu/messaging_menu.xml b/addons/mail/static/src/components/messaging_menu/messaging_menu.xml index e9e56f7c906..66bafea4bad 100644 --- a/addons/mail/static/src/components/messaging_menu/messaging_menu.xml +++ b/addons/mail/static/src/components/messaging_menu/messaging_menu.xml @@ -2,8 +2,8 @@ -
  • - + -
  • +
    diff --git a/addons/mail/static/src/components/messaging_menu/tests/messaging_menu_tests.js b/addons/mail/static/src/components/messaging_menu/tests/messaging_menu_tests.js index f773ce7c05e..756c193a224 100644 --- a/addons/mail/static/src/components/messaging_menu/tests/messaging_menu_tests.js +++ b/addons/mail/static/src/components/messaging_menu/tests/messaging_menu_tests.js @@ -201,7 +201,7 @@ QUnit.test('basic rendering', async function (assert) { await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click()); assert.hasClass( document.querySelector('.o_MessagingMenu'), - "o-is-open", + "show", "should mark messaging menu as opened" ); assert.strictEqual( @@ -280,7 +280,7 @@ QUnit.test('basic rendering', async function (assert) { await afterNextRender(() => document.querySelector(`.o_MessagingMenu_toggler`).click()); assert.doesNotHaveClass( document.querySelector('.o_MessagingMenu'), - "o-is-open", + "show", "should mark messaging menu as closed" ); }); diff --git a/addons/mail/static/src/components/rtc_activity_notice/rtc_activity_notice.xml b/addons/mail/static/src/components/rtc_activity_notice/rtc_activity_notice.xml index 5379d073179..81ea5e4a35c 100644 --- a/addons/mail/static/src/components/rtc_activity_notice/rtc_activity_notice.xml +++ b/addons/mail/static/src/components/rtc_activity_notice/rtc_activity_notice.xml @@ -2,7 +2,7 @@ - +
  • +
  • diff --git a/addons/mail/static/src/widgets/rtc_activity_notice/rtc_activity_notice.xml b/addons/mail/static/src/widgets/rtc_activity_notice/rtc_activity_notice.xml index c30a12bc03e..d5eb221310e 100644 --- a/addons/mail/static/src/widgets/rtc_activity_notice/rtc_activity_notice.xml +++ b/addons/mail/static/src/widgets/rtc_activity_notice/rtc_activity_notice.xml @@ -2,7 +2,7 @@ -
  • +
    diff --git a/addons/mail/static/src/xml/systray.xml b/addons/mail/static/src/xml/systray.xml index 55a5c5c6614..ddfbee91c0c 100644 --- a/addons/mail/static/src/xml/systray.xml +++ b/addons/mail/static/src/xml/systray.xml @@ -49,13 +49,13 @@ -
  • -
  • +
    diff --git a/addons/mail/static/tests/activity_tests.js b/addons/mail/static/tests/activity_tests.js index 7d41db1da5b..780fab2a5d4 100644 --- a/addons/mail/static/tests/activity_tests.js +++ b/addons/mail/static/tests/activity_tests.js @@ -402,16 +402,16 @@ QUnit.test("activity view: no group_by_menu and no comparison_menu", async funct assert.containsN( webClient, - ".o_search_options .o_dropdown button:visible", + ".o_search_options .dropdown button:visible", 2, "only two elements should be available in view search" ); assert.isVisible( - $(webClient.el).find(".o_search_options .o_dropdown.o_filter_menu > button"), + $(webClient.el).find(".o_search_options .dropdown.o_filter_menu > button"), "filter should be available in view search" ); assert.isVisible( - $(webClient.el).find(".o_search_options .o_dropdown.o_favorite_menu > button"), + $(webClient.el).find(".o_search_options .dropdown.o_favorite_menu > button"), "favorites should be available in view search" ); }); diff --git a/addons/mail/static/tests/tools/debug_manager_tests.js b/addons/mail/static/tests/tools/debug_manager_tests.js index 0a50e4cd123..f3a7e667ebf 100644 --- a/addons/mail/static/tests/tools/debug_manager_tests.js +++ b/addons/mail/static/tests/tools/debug_manager_tests.js @@ -40,10 +40,10 @@ QUnit.test("Manage Messages", async function (assert) { const wc = await createWebClient({ serverData, mockRPC }); await doAction(wc, 3, { viewType: "form", props: { resId: 5 } }); await legacyExtraNextTick(); - await click(wc.el, ".o_debug_manager .o_dropdown_toggler"); + await click(wc.el, ".o_debug_manager .dropdown-toggle"); const dropdownItems = wc.el.querySelectorAll( - ".o_debug_manager .o_dropdown_menu .o_dropdown_item" + ".o_debug_manager .dropdown-menu .dropdown-item" ); assert.strictEqual(dropdownItems.length, 1); assert.strictEqual( diff --git a/addons/sale_timesheet/static/tests/tours/sale_timesheet_tour.js b/addons/sale_timesheet/static/tests/tours/sale_timesheet_tour.js index 6e1fa4176f7..6aefb825e58 100644 --- a/addons/sale_timesheet/static/tests/tours/sale_timesheet_tour.js +++ b/addons/sale_timesheet/static/tests/tours/sale_timesheet_tour.js @@ -156,7 +156,7 @@ tour.register('sale_timesheet_tour', { content: 'Click on the Configuration menu.', run: 'click', }, { - trigger: 'li[data-menu-xmlid="project.menu_projects_config"]', + trigger: '.dropdown-item[data-menu-xmlid="project.menu_projects_config"]', content: 'Select Configuration > Projects.', run: 'click', }, { @@ -238,7 +238,7 @@ tour.register('sale_timesheet_tour', { trigger: '.o_form_button_save', content: 'Save Project', }, { - trigger: 'li[data-menu-xmlid="project.menu_projects"]', + trigger: '.dropdown-item[data-menu-xmlid="project.menu_projects"]', content: 'Select Projects', }, { // an invisible element cannot be used as a trigger so this small hack is mandatory for the next step diff --git a/addons/stock/static/src/xml/report_stock_forecasted.xml b/addons/stock/static/src/xml/report_stock_forecasted.xml index 2c294cf4b09..20edf8c3ad2 100644 --- a/addons/stock/static/src/xml/report_stock_forecasted.xml +++ b/addons/stock/static/src/xml/report_stock_forecasted.xml @@ -8,13 +8,13 @@ -
    - - diff --git a/addons/web/static/src/core/dropdown/dropdown_item.js b/addons/web/static/src/core/dropdown/dropdown_item.js index c51361aec57..9d321cc8dab 100644 --- a/addons/web/static/src/core/dropdown/dropdown_item.js +++ b/addons/web/static/src/core/dropdown/dropdown_item.js @@ -27,8 +27,11 @@ export const ParentClosingMode = { export class DropdownItem extends Component { /** * Triggers a custom DropdownItemSelectedEvent + * @param {MouseEvent} ev */ - onClick() { + onClick(ev) { + ev.preventDefault(); + /** @type DropdownItemSelectedEventDetail */ const detail = { payload: this.props.payload, @@ -54,6 +57,10 @@ DropdownItem.props = { type: String, optional: true, }, + href: { + type: String, + optional: true, + }, title: { type: String, optional: true, diff --git a/addons/web/static/src/core/dropdown/dropdown_item.xml b/addons/web/static/src/core/dropdown/dropdown_item.xml index f1533698cf6..34163198921 100644 --- a/addons/web/static/src/core/dropdown/dropdown_item.xml +++ b/addons/web/static/src/core/dropdown/dropdown_item.xml @@ -2,14 +2,16 @@ -
  • -
  • +
    diff --git a/addons/web/static/src/core/dropdown/dropdown_navigation_hook.js b/addons/web/static/src/core/dropdown/dropdown_navigation_hook.js index 40ff77bc486..37c4bb7a09a 100644 --- a/addons/web/static/src/core/dropdown/dropdown_navigation_hook.js +++ b/addons/web/static/src/core/dropdown/dropdown_navigation_hook.js @@ -10,6 +10,7 @@ import { scrollTo } from "../utils/scrolling"; * el: HTMLElement, * isActive: boolean, * makeOnlyActive: ()=>void, + * navTarget: HTMLElement, * isSubDropdown: boolean, * isSubDropdownOpen: boolean, * closeSubDropdown: ()=>void, @@ -21,7 +22,7 @@ const { hooks } = owl; const { useComponent, useRef } = hooks; const ACTIVE_MENU_ELEMENT_CLASS = "focus"; -const MENU_ELEMENTS_SELECTORS = [":scope > li.o_dropdown_item", ":scope > li.o_dropdown"]; +const MENU_ELEMENTS_SELECTORS = [":scope > .dropdown-item", ":scope > .dropdown"]; const NEXT_ACTIVE_INDEX_FNS = { FIRST: () => 0, LAST: (list) => list.length - 1, @@ -33,12 +34,30 @@ export function useDropdownNavigation() { /** @type {import("./dropdown").Dropdown} */ const comp = useComponent(); - // As this navigation hook relies on clicking ".o_dropdown_toggler" elements, + // As this navigation hook relies on clicking ".dropdown-toggle" elements, // it is incompatible with a toggler="parent" strategy for subdropdowns. if (comp.hasParentDropdown && comp.props.toggler === "parent") { throw new Error("A nested Dropdown must use its standard toggler"); } + // Needed to avoid unwanted mouseclick behavior on a subdropdown toggler. + const originalOnTogglerClick = comp.onTogglerClick.bind(comp); + comp.onTogglerClick = (ev) => { + if (comp.hasParentDropdown && !ev.__fromDropdownNavigation) { + return; + } + originalOnTogglerClick(); + }; + + // Needed to avoid unwanted mouseenter behavior on a subdropdown toggler. + const originalOnTogglerMouseEnter = comp.onTogglerMouseEnter.bind(comp); + comp.onTogglerMouseEnter = () => { + if (comp.hasParentDropdown) { + return; + } + originalOnTogglerMouseEnter(); + }; + // Needed to avoid unwanted selection when the mouse pointer is not in use // but still somewhere in the middle of the dropdown menu list. let mouseSelectionActive = true; @@ -56,20 +75,17 @@ export function useDropdownNavigation() { /** @type {NodeListOf} */ const queryResult = menuRef.el.querySelectorAll(MENU_ELEMENTS_SELECTORS.join()); for (const el of queryResult) { - const isSubDropdown = el.classList.contains("o_dropdown"); + const isSubDropdown = el.classList.contains("dropdown"); const isSubDropdownOpen = () => el.classList.contains("show"); - const togglerClick = () => { - const toggler = el.querySelector(":scope > .o_dropdown_toggler"); - if (toggler) { - toggler.dispatchEvent(new MouseEvent("click", { bubbles: false })); - } - }; + const navTarget = isSubDropdown ? el.querySelector(":scope > .dropdown-toggle") : el; let subDropdownTimeout; const closeSubDropdown = () => { browser.clearTimeout(subDropdownTimeout); subDropdownTimeout = browser.setTimeout(() => { if (isSubDropdownOpen()) { - togglerClick(); + const ev = new MouseEvent("click", { bubbles: false }); + ev.__fromDropdownNavigation = true; + navTarget.dispatchEvent(ev); } }, 200); }; @@ -78,7 +94,9 @@ export function useDropdownNavigation() { subDropdownTimeout = browser.setTimeout( () => { if (!isSubDropdownOpen()) { - togglerClick(); + const ev = new MouseEvent("click", { bubbles: false }); + ev.__fromDropdownNavigation = true; + navTarget.dispatchEvent(ev); } }, immediate ? 0 : 200 @@ -90,22 +108,23 @@ export function useDropdownNavigation() { if (menuElement.el === el) { continue; } - menuElement.el.classList.remove(ACTIVE_MENU_ELEMENT_CLASS); + menuElement.navTarget.classList.remove(ACTIVE_MENU_ELEMENT_CLASS); if (menuElement.isSubDropdown) { menuElement.closeSubDropdown(); } } // Make myself active - el.classList.add(ACTIVE_MENU_ELEMENT_CLASS); + navTarget.classList.add(ACTIVE_MENU_ELEMENT_CLASS); }; /** @type {MenuElement} */ const menuElement = { el, get isActive() { - return el.classList.contains(ACTIVE_MENU_ELEMENT_CLASS); + return navTarget.classList.contains(ACTIVE_MENU_ELEMENT_CLASS); }, makeOnlyActive, + navTarget, get isSubDropdownOpen() { return isSubDropdownOpen(); }, @@ -117,12 +136,11 @@ export function useDropdownNavigation() { // Set up selection listeners const elementListeners = { - click: makeOnlyActive, mouseenter: () => { if (!mouseSelectionActive) { mouseSelectionActive = true; } else { - menuElement.makeOnlyActive(); + makeOnlyActive(); if (isSubDropdown) { openSubDropdown(); } @@ -130,18 +148,18 @@ export function useDropdownNavigation() { }, }; for (const [eventType, listener] of Object.entries(elementListeners)) { - menuElement.el.addEventListener(eventType, listener); + navTarget.addEventListener(eventType, listener); } - addedListeners.push([menuElement, elementListeners]); + addedListeners.push([navTarget, elementListeners]); } return () => { menuElements = []; mouseSelectionActive = true; // Clear mouse selection listeners - for (const [menuElement, listeners] of addedListeners) { + for (const [navTarget, listeners] of addedListeners) { for (const [eventType, listener] of Object.entries(listeners)) { - menuElement.el.removeEventListener(eventType, listener); + navTarget.removeEventListener(eventType, listener); } } }; @@ -191,6 +209,16 @@ export function useDropdownNavigation() { menuElement.openSubDropdown(true); } }; + const selectActiveMenuElement = () => { + const menuElement = getActiveMenuElement(); + if (menuElement) { + if (menuElement.isSubDropdown) { + menuElement.openSubDropdown(true); + } else { + menuElement.navTarget.click(); + } + } + }; let hotkeyRemoves = []; const hotkeyCallbacks = { home: () => setActiveMenuElement("FIRST"), @@ -201,16 +229,7 @@ export function useDropdownNavigation() { arrowup: () => setActiveMenuElement("PREV"), arrowleft: localization.direction === "rtl" ? openSubDropdown : closeSubDropdown, arrowright: localization.direction === "rtl" ? closeSubDropdown : openSubDropdown, - enter: () => { - const menuElement = getActiveMenuElement(); - if (menuElement) { - if (menuElement.isSubDropdown) { - menuElement.openSubDropdown(true); - } else { - menuElement.el.click(); - } - } - }, + enter: selectActiveMenuElement, escape: comp.close, }; useEffect( diff --git a/addons/web/static/src/legacy/js/fields/relational_fields.js b/addons/web/static/src/legacy/js/fields/relational_fields.js index 876bf19d5c8..b943a398aff 100644 --- a/addons/web/static/src/legacy/js/fields/relational_fields.js +++ b/addons/web/static/src/legacy/js/fields/relational_fields.js @@ -333,6 +333,18 @@ var FieldMany2One = AbstractField.extend({ html: true, minLength: 0, delay: this.AUTOCOMPLETE_DELAY, + classes: { + "ui-autocomplete": "dropdown-menu", + }, + create: function() { + $(this).data('ui-autocomplete')._renderMenu = function(ulWrapper, entries) { + var render = this; + $.each(entries, function(index, entry) { + render._renderItemData(ulWrapper, entry); + }); + $(ulWrapper).find( "li > a" ).addClass( "dropdown-item" ); + } + }, }); this.$input.autocomplete("option", "position", { my : "left top", at: "left bottom" }); this.autocomplete_bound = true; diff --git a/addons/web/static/src/legacy/js/views/graph/graph_controller.js b/addons/web/static/src/legacy/js/views/graph/graph_controller.js index bbd5d490b35..23b274ee697 100644 --- a/addons/web/static/src/legacy/js/views/graph/graph_controller.js +++ b/addons/web/static/src/legacy/js/views/graph/graph_controller.js @@ -193,7 +193,7 @@ var GraphController = AbstractController.extend({ this.groupByMenu.el.classList.add('o_group_by_menu'); } // Update button classes accordingly to the current mode - const buttons = actionsContainer.querySelectorAll('.o_dropdown_toggler'); + const buttons = actionsContainer.querySelectorAll('.dropdown-toggle'); for (const button of buttons) { button.classList.remove('btn-secondary'); if (this.isEmbedded) { diff --git a/addons/web/static/src/legacy/scss/bootstrap_overridden.scss b/addons/web/static/src/legacy/scss/bootstrap_overridden.scss index c1e3e70d954..63a22b729f5 100644 --- a/addons/web/static/src/legacy/scss/bootstrap_overridden.scss +++ b/addons/web/static/src/legacy/scss/bootstrap_overridden.scss @@ -33,8 +33,6 @@ $danger: $o-danger !default; // // Quickly modify global styling by enabling or disabling optional features. -$enable-caret: false !default; // Disable pseudo element caret on .dropdown-toggle. - // Body // // Settings for the `` element. diff --git a/addons/web/static/src/legacy/scss/dropdown.scss b/addons/web/static/src/legacy/scss/dropdown.scss index 21cda1c543f..d89e0c14a7f 100644 --- a/addons/web/static/src/legacy/scss/dropdown.scss +++ b/addons/web/static/src/legacy/scss/dropdown.scss @@ -1,7 +1,6 @@ // = jQueryUI dropdown adaptations // ---------------------------------------------------------------------------- .ui-widget.ui-autocomplete { - @extend .dropdown-menu; top: 0; // Compute initial position min-width: 0; // Compute initial width @@ -10,11 +9,10 @@ z-index: $zindex-modal + 1; .ui-menu-item { - padding: 0; - > a { - @extend .dropdown-item; - + &.dropdown-item { + padding: $o-dropdown-vpadding $o-dropdown-hpadding; + } &.ui-state-active { margin: 0; border: none; diff --git a/addons/web/static/src/legacy/scss/navbar.scss b/addons/web/static/src/legacy/scss/navbar.scss index 28e3dd95ead..2bae3381567 100644 --- a/addons/web/static/src/legacy/scss/navbar.scss +++ b/addons/web/static/src/legacy/scss/navbar.scss @@ -44,6 +44,7 @@ height: $o-navbar-height; cursor: pointer; user-select: none; + background: transparent; @include o-hover-text-color(rgba($white, .9), $white); } @@ -61,8 +62,6 @@ %-main-navbar-entry-active { @include plain-hover-focus() { background: $-backend-entry-hover-bg; - outline: 1px solid $o-navbar-inverse-link-hover-bg; - outline-offset: -1px; color: $white; } } @@ -77,54 +76,53 @@ // = Owl components // -------------------------------------------------------------------------- - .o_dropdown .o_dropdown_toggler { - @extend %-main-navbar-entry-base; - @extend %-main-navbar-entry-padding; - @extend %-main-navbar-entry-bg-hover; - border: 0; - white-space: nowrap; + &,.o_menu_sections, .o_menu_systray { - &, &:focus { - background: none; - } - } - - .o-dropdown { - &.show > .o_dropdown_toggler { - @extend %-main-navbar-entry-active; - } - } - - .o_nav_entry { - padding: 0; - @extend %-main-navbar-entry-base; - @extend %-main-navbar-entry-padding; - } - - .o_dropdown .o_dropdown_menu, .dropdown-menu { - margin-top: 0; - border-top: 0; - @include border-top-radius(0); - } - - .o_dropdown_menu_group_entry.o_dropdown_item { - > *:first-child { - padding-left: $o-dropdown-hpadding * 1.5; + .dropdown .dropdown-toggle { + @extend %-main-navbar-entry-base; + @extend %-main-navbar-entry-padding; + @extend %-main-navbar-entry-bg-hover; + border: 0; + white-space: nowrap; } - + .o_dropdown_item:not(.o_dropdown_menu_group_entry) { - margin-top: .8em; + .dropdown { + &.show > .dropdown-toggle { + @extend %-main-navbar-entry-active; + } + } + + .o_nav_entry { + @extend %-main-navbar-entry-base; + @extend %-main-navbar-entry-padding; + @extend %-main-navbar-entry-bg-hover; + } + + .dropdown-menu { + margin-top: 0; + border-top: 0; + @include border-top-radius(0); + } + + .o_dropdown_menu_group_entry { + > .dropdown-item { + padding-left: $o-dropdown-hpadding * 1.5; + } + + + .dropdown-item:not(.o_dropdown_menu_group_entry) { + margin-top: .8em; + } } } // = Legacy elements adaptations // -------------------------------------------------------------------------- - > ul { - > li > a, > li > label:only-child, .o_dropdown_toggler { + > .o_menu_sections, .o_menu_systray { + > div > a, > div > label:only-child, .dropdown-toggle, .dropdown-toggle.o-dropdown--narrow { padding: 0 $dropdown-item-padding-x * .5; } - > li { + > div { &, > a { @extend %-main-navbar-entry-base; } @@ -138,7 +136,6 @@ } } } - // = Main Navbar Elements // -------------------------------------------------------------------------- .o_menu_toggle, .o_navbar_apps_menu { diff --git a/addons/web/static/src/legacy/scss/utils.scss b/addons/web/static/src/legacy/scss/utils.scss index 0608ea491b0..f5e10117abc 100644 --- a/addons/web/static/src/legacy/scss/utils.scss +++ b/addons/web/static/src/legacy/scss/utils.scss @@ -383,9 +383,15 @@ padding: 0; > a { - @extend .dropdown-item; margin: auto auto auto (-$link-padding-gap); padding: 3px $link-padding-gap; + color: $dropdown-link-color; + display: block; + + &:hover { + background-color: $dropdown-link-hover-bg; + color: $dropdown-link-hover-color; + } } &:last-child { diff --git a/addons/web/static/src/legacy/xml/base.xml b/addons/web/static/src/legacy/xml/base.xml index 587126d3a27..19aedeac394 100644 --- a/addons/web/static/src/legacy/xml/base.xml +++ b/addons/web/static/src/legacy/xml/base.xml @@ -152,9 +152,9 @@ -
    + +
    - +
    diff --git a/addons/web/static/src/search/favorite_menu/favorite_menu.xml b/addons/web/static/src/search/favorite_menu/favorite_menu.xml index 4631765e594..50e359d7b60 100644 --- a/addons/web/static/src/search/favorite_menu/favorite_menu.xml +++ b/addons/web/static/src/search/favorite_menu/favorite_menu.xml @@ -2,10 +2,7 @@ - + Favorites @@ -13,10 +10,10 @@ - +
    - +
    - +
    diff --git a/addons/web/static/src/search/filter_menu/filter_menu.xml b/addons/web/static/src/search/filter_menu/filter_menu.xml index a3fe871b295..56949943b31 100644 --- a/addons/web/static/src/search/filter_menu/filter_menu.xml +++ b/addons/web/static/src/search/filter_menu/filter_menu.xml @@ -2,10 +2,7 @@ - + Filters @@ -13,44 +10,42 @@ -
  • +
    -
  • - +
    +
    - +
    diff --git a/addons/web/static/src/search/group_by_menu/group_by_menu.xml b/addons/web/static/src/search/group_by_menu/group_by_menu.xml index c606555dd9b..9efe09be6f7 100644 --- a/addons/web/static/src/search/group_by_menu/group_by_menu.xml +++ b/addons/web/static/src/search/group_by_menu/group_by_menu.xml @@ -3,10 +3,10 @@ + > Group By @@ -14,43 +14,41 @@ -
  • coucou
  • '); + assert.strictEqual(parent.el.outerHTML, 'coucou'); + }); + + QUnit.test("DropdownItem (with href prop) can be rendered as ", async (assert) => { + class Parent extends owl.Component {} + Parent.template = owl.tags.xml`coucou`; + env = await makeTestEnv(); + parent = await mount(Parent, { env, target }); + assert.strictEqual(parent.el.outerHTML, 'coucou'); + }); + + QUnit.test("DropdownItem: prevents click default when item clicked", async (assert) => { + assert.expect(2); + // A DropdownItem should preventDefault a click as it may take the shape + // of an tag with an [href] attribute and e.g. could change the url when clicked. + patchWithCleanup(DropdownItem.prototype, { + onClick(ev) { + assert.ok(!ev.defaultPrevented); + this._super(...arguments); + assert.ok(ev.defaultPrevented); + }, + }); + class Parent extends owl.Component {} + Parent.template = owl.tags.xml``; + env = await makeTestEnv(); + parent = await mount(Parent, { env, target }); + await click(target, ".dropdown-item"); }); QUnit.test("can be styled", async (assert) => { @@ -75,14 +95,14 @@ QUnit.module("Components", ({ beforeEach }) => { `; env = await makeTestEnv(); parent = await mount(Parent, { env, target }); - await click(parent.el, "button.o_dropdown_toggler"); - assert.hasClass(parent.el, "o_dropdown one"); - const toggler = parent.el.querySelector("button"); - assert.hasClass(toggler, "o_dropdown_toggler two"); - const menu = parent.el.querySelector("ul"); - assert.hasClass(menu, "o_dropdown_menu three"); - const item = parent.el.querySelector("li"); - assert.hasClass(item, "o_dropdown_item four"); + await click(parent.el, "button.dropdown-toggle"); + assert.hasClass(parent.el, "dropdown one"); + const toggler = parent.el.querySelector(".dropdown-toggle"); + assert.hasClass(toggler, "two"); + const menu = parent.el.querySelector(".dropdown-menu"); + assert.hasClass(menu, "three"); + const item = parent.el.querySelector(".dropdown-item"); + assert.hasClass(item, "four"); }); QUnit.test("menu can be toggled", async (assert) => { @@ -100,14 +120,14 @@ QUnit.module("Components", ({ beforeEach }) => { Parent.template = owl.tags.xml``; env = await makeTestEnv(); parent = await mount(Parent, { env, target }); - await click(parent.el, "button.o_dropdown_toggler"); + await click(parent.el, "button.dropdown-toggle"); assert.verifySteps(["beforeOpen"]); - assert.containsNone(parent.el, "ul.o_dropdown_menu"); + assert.containsNone(parent.el, ".dropdown-menu"); beforeOpenProm.resolve(); await nextTick(); - assert.containsOnce(parent.el, "ul.o_dropdown_menu"); - await click(parent.el, "button.o_dropdown_toggler"); - assert.containsNone(parent.el, "ul.o_dropdown_menu"); + assert.containsOnce(parent.el, ".dropdown-menu"); + await click(parent.el, "button.dropdown-toggle"); + assert.containsNone(parent.el, ".dropdown-menu"); }); QUnit.test("initial open state can be true", async (assert) => { @@ -124,7 +144,7 @@ QUnit.module("Components", ({ beforeEach }) => { env = await makeTestEnv(); parent = await mount(Parent, { env, target }); assert.verifySteps(["beforeOpen"]); - assert.containsOnce(parent.el, "ul.o_dropdown_menu"); + assert.containsOnce(parent.el, ".dropdown-menu"); }); QUnit.test("close on outside click", async (assert) => { @@ -137,10 +157,10 @@ QUnit.module("Components", ({ beforeEach }) => { `; env = await makeTestEnv(); parent = await mount(Parent, { env, target }); - await click(parent.el, "button.o_dropdown_toggler"); - assert.containsOnce(parent.el, "ul.o_dropdown_menu"); + await click(parent.el, "button.dropdown-toggle"); + assert.containsOnce(parent.el, ".dropdown-menu"); await click(parent.el, "div.outside"); - assert.containsNone(parent.el, "ul.o_dropdown_menu"); + assert.containsNone(parent.el, ".dropdown-menu"); }); QUnit.test("close on item selection", async (assert) => { @@ -152,9 +172,9 @@ QUnit.module("Components", ({ beforeEach }) => { `; env = await makeTestEnv(); parent = await mount(Parent, { env, target }); - await click(parent.el, "button.o_dropdown_toggler"); - await click(parent.el, "ul.o_dropdown_menu li"); - assert.containsNone(parent.el, "ul.o_dropdown_menu"); + await click(parent.el, "button.dropdown-toggle"); + await click(parent.el, ".dropdown-menu .dropdown-item"); + assert.containsNone(parent.el, ".dropdown-menu"); }); QUnit.test("payload received on item selection", async (assert) => { @@ -170,8 +190,8 @@ QUnit.module("Components", ({ beforeEach }) => { `; env = await makeTestEnv(); parent = await mount(Parent, { env, target }); - await click(parent.el, "button.o_dropdown_toggler"); - await click(parent.el, "ul.o_dropdown_menu li"); + await click(parent.el, "button.dropdown-toggle"); + await click(parent.el, ".dropdown-menu .dropdown-item"); }); QUnit.test("multi-level dropdown: can be rendered and toggled", async (assert) => { @@ -185,10 +205,10 @@ QUnit.module("Components", ({ beforeEach }) => { `; env = await makeTestEnv(); parent = await mount(Parent, { env, target }); - await click(parent.el, "button.o_dropdown_toggler:last-child"); - await click(parent.el, "button.o_dropdown_toggler:last-child"); - await click(parent.el, "button.o_dropdown_toggler:last-child"); - assert.containsN(parent.el, "ul.o_dropdown_menu", 3); + await click(parent.el, "button.dropdown-toggle:last-child"); + await mouseEnter(parent.el, "button.dropdown-toggle:last-child"); + await mouseEnter(parent.el, "button.dropdown-toggle:last-child"); + assert.containsN(parent.el, ".dropdown-menu", 3); }); QUnit.test("multi-level dropdown: initial open state can be true", async (assert) => { @@ -202,7 +222,7 @@ QUnit.module("Components", ({ beforeEach }) => { `; env = await makeTestEnv(); parent = await mount(Parent, { env, target }); - assert.containsN(parent.el, "ul.o_dropdown_menu", 3); + assert.containsN(parent.el, ".dropdown-menu", 3); }); QUnit.test("multi-level dropdown: close on outside click", async (assert) => { @@ -219,12 +239,12 @@ QUnit.module("Components", ({ beforeEach }) => { `; env = await makeTestEnv(); parent = await mount(Parent, { env, target }); - await click(parent.el, "button.o_dropdown_toggler:last-child"); - await click(parent.el, "button.o_dropdown_toggler:last-child"); - await click(parent.el, "button.o_dropdown_toggler:last-child"); - assert.containsN(parent.el, "ul.o_dropdown_menu", 3); + await click(parent.el, "button.dropdown-toggle:last-child"); + await mouseEnter(parent.el, "button.dropdown-toggle:last-child"); + await mouseEnter(parent.el, "button.dropdown-toggle:last-child"); + assert.containsN(parent.el, ".dropdown-menu", 3); await click(parent.el, "div.outside"); - assert.containsNone(parent.el, "ul.o_dropdown_menu"); + assert.containsNone(parent.el, ".dropdown-menu"); }); QUnit.test("multi-level dropdown: close on item selection", async (assert) => { @@ -238,11 +258,17 @@ QUnit.module("Components", ({ beforeEach }) => { `; env = await makeTestEnv(); parent = await mount(Parent, { env, target }); - await click(parent.el, "button.o_dropdown_toggler:last-child"); - await click(parent.el, "button.o_dropdown_toggler:last-child"); - assert.containsN(parent.el, "ul.o_dropdown_menu", 2); - await click(parent.el, ".o_dropdown_item:not(.o_dropdown)"); - assert.containsNone(parent.el, "ul.o_dropdown_menu"); + await click(parent.el, "button.dropdown-toggle:last-child"); + await mouseEnter(parent.el, "button.dropdown-toggle:last-child"); + assert.containsN(parent.el, ".dropdown-menu", 2); + assert.containsN(parent.el, ".dropdown-item", 2); + assert.containsOnce( + parent.el, + ".dropdown-menu > .dropdown > .dropdown-toggle.dropdown-item" + ); + assert.containsOnce(parent.el, ".dropdown-menu > .dropdown-item"); + await click(parent.el, ".dropdown-menu > .dropdown-item"); + assert.containsNone(parent.el, ".dropdown-menu"); }); QUnit.test("multi-level dropdown: parent closing modes on item selection", async (assert) => { @@ -260,28 +286,28 @@ QUnit.module("Components", ({ beforeEach }) => { env = await makeTestEnv(); parent = await mount(Parent, { env, target }); // Open the 2-level dropdowns - await click(parent.el, "button.o_dropdown_toggler:last-child"); - await click(parent.el, "button.o_dropdown_toggler:last-child"); - assert.containsN(parent.el, "ul.o_dropdown_menu", 2); + await click(parent.el, "button.dropdown-toggle:last-child"); + await mouseEnter(parent.el, "button.dropdown-toggle:last-child"); + assert.containsN(parent.el, ".dropdown-menu", 2); // Select item (parentClosingMode=none) - await click(parent.el, "li.item1"); - assert.containsN(parent.el, "ul.o_dropdown_menu", 2); + await click(parent.el, ".item1"); + assert.containsN(parent.el, ".dropdown-menu", 2); // Select item (parentClosingMode=closest) - await click(parent.el, "li.item2"); - assert.containsN(parent.el, "ul.o_dropdown_menu", 1); + await click(parent.el, ".item2"); + assert.containsN(parent.el, ".dropdown-menu", 1); // Reopen second level dropdown - await click(parent.el, "button.o_dropdown_toggler:last-child"); - assert.containsN(parent.el, "ul.o_dropdown_menu", 2); + await mouseEnter(parent.el, "button.dropdown-toggle:last-child"); + assert.containsN(parent.el, ".dropdown-menu", 2); // Select item (parentClosingMode=all) - await click(parent.el, "li.item3"); - assert.containsNone(parent.el, "ul.o_dropdown_menu"); + await click(parent.el, ".item3"); + assert.containsNone(parent.el, ".dropdown-menu"); // Reopen the 2-level dropdowns - await click(parent.el, "button.o_dropdown_toggler:last-child"); - await click(parent.el, "button.o_dropdown_toggler:last-child"); - assert.containsN(parent.el, "ul.o_dropdown_menu", 2); + await click(parent.el, "button.dropdown-toggle:last-child"); + await mouseEnter(parent.el, "button.dropdown-toggle:last-child"); + assert.containsN(parent.el, ".dropdown-menu", 2); // Select item (default should be parentClosingMode=all) - await click(parent.el, "li.item4"); - assert.containsNone(parent.el, "ul.o_dropdown_menu"); + await click(parent.el, ".item4"); + assert.containsNone(parent.el, ".dropdown-menu"); }); QUnit.test("multi-level dropdown: payload bubbles on item selection", async (assert) => { @@ -300,11 +326,11 @@ QUnit.module("Components", ({ beforeEach }) => { `; env = await makeTestEnv(); parent = await mount(Parent, { env, target }); - await click(parent.el, "button.o_dropdown_toggler:last-child"); - await click(parent.el, "button.o_dropdown_toggler:last-child"); + await click(parent.el, "button.dropdown-toggle:last-child"); + await mouseEnter(parent.el, "button.dropdown-toggle:last-child"); // As two listeners are defined in the template, // clicking once the item would execute the handler twice. - await click(parent.el, ".o_dropdown_item:not(.o_dropdown)"); + await click(parent.el, ".dropdown-menu > .dropdown-item"); }); QUnit.test("multi-level dropdown: recursive template can be rendered", async (assert) => { @@ -364,7 +390,7 @@ QUnit.module("Components", ({ beforeEach }) => { env.qweb.addTemplate("recursive.Template", recursiveTemplate); parent = await mount(Parent, { env, target }); assert.deepEqual( - [...parent.el.querySelectorAll("button,.o_dropdown_item:not(.o_dropdown)")].map( + [...parent.el.querySelectorAll("button,.dropdown-menu > .dropdown-item")].map( (el) => el.textContent ), [ @@ -413,34 +439,34 @@ QUnit.module("Components", ({ beforeEach }) => { const one = parent.el.querySelector(".one"); await click(one, "button"); assert.verifySteps([]); - assert.containsOnce(parent.el, "ul.o_dropdown_menu"); - assert.containsOnce(one, "ul.o_dropdown_menu"); + assert.containsOnce(parent.el, ".dropdown-menu"); + assert.containsOnce(one, ".dropdown-menu"); // Hover on TWO const two = parent.el.querySelector(".two"); two.querySelector("button").dispatchEvent(new MouseEvent("mouseenter")); assert.verifySteps(["beforeOpen"]); await nextTick(); - assert.containsOnce(parent.el, "ul.o_dropdown_menu"); - assert.containsNone(two, "ul.o_dropdown_menu"); + assert.containsOnce(parent.el, ".dropdown-menu"); + assert.containsNone(two, ".dropdown-menu"); beforeOpenProm.resolve(); await nextTick(); - assert.containsOnce(parent.el, "ul.o_dropdown_menu"); - assert.containsOnce(two, "ul.o_dropdown_menu"); + assert.containsOnce(parent.el, ".dropdown-menu"); + assert.containsOnce(two, ".dropdown-menu"); // Hover on THREE const three = parent.el.querySelector(".three"); three.querySelector("button").dispatchEvent(new MouseEvent("mouseenter")); await nextTick(); - assert.containsOnce(parent.el, "ul.o_dropdown_menu"); - assert.containsOnce(three, "ul.o_dropdown_menu"); + assert.containsOnce(parent.el, ".dropdown-menu"); + assert.containsOnce(three, ".dropdown-menu"); // Click on OUTSIDE await click(parent.el, "div.outside"); - assert.containsNone(parent.el, "ul.o_dropdown_menu"); + assert.containsNone(parent.el, ".dropdown-menu"); // Hover on ONE, TWO, THREE one.querySelector("button").dispatchEvent(new MouseEvent("mouseenter")); two.querySelector("button").dispatchEvent(new MouseEvent("mouseenter")); three.querySelector("button").dispatchEvent(new MouseEvent("mouseenter")); await nextTick(); - assert.containsNone(parent.el, "ul.o_dropdown_menu"); + assert.containsNone(parent.el, ".dropdown-menu"); } ); @@ -459,13 +485,13 @@ QUnit.module("Components", ({ beforeEach }) => { parent = await mount(Parent, { env, target }); // Click on FOO await click(parent.el, ".foo button"); - assert.containsOnce(parent.el, "ul.o_dropdown_menu"); + assert.containsOnce(parent.el, ".dropdown-menu"); // Hover on BAR1 const bar1 = parent.el.querySelector(".bar1"); bar1.querySelector("button").dispatchEvent(new MouseEvent("mouseenter")); await nextTick(); - assert.containsOnce(parent.el, "ul.o_dropdown_menu"); - assert.containsNone(bar1, "ul.o_dropdown_menu"); + assert.containsOnce(parent.el, ".dropdown-menu"); + assert.containsNone(bar1, ".dropdown-menu"); } ); @@ -484,16 +510,16 @@ QUnit.module("Components", ({ beforeEach }) => { parent = await mount(Parent, { env, target }); // Click on BAR1 await click(parent.el, ".bar1 button"); - assert.containsOnce(parent.el, "ul.o_dropdown_menu"); + assert.containsOnce(parent.el, ".dropdown-menu"); // Click on FOO await click(parent.el, ".foo button"); - assert.containsOnce(parent.el, "ul.o_dropdown_menu"); + assert.containsOnce(parent.el, ".dropdown-menu"); // Hover on BAR1 const bar1 = parent.el.querySelector(".bar1"); bar1.querySelector("button").dispatchEvent(new MouseEvent("mouseenter")); await nextTick(); - assert.containsOnce(parent.el, "ul.o_dropdown_menu"); - assert.containsNone(bar1, "ul.o_dropdown_menu"); + assert.containsOnce(parent.el, ".dropdown-menu"); + assert.containsNone(bar1, ".dropdown-menu"); } ); @@ -511,23 +537,23 @@ QUnit.module("Components", ({ beforeEach }) => { parent = await mount(Parent, { env, target }); // Click on one await click(parent.el, ".one button"); - assert.containsOnce(parent.el, "ul.o_dropdown_menu"); + assert.containsOnce(parent.el, ".dropdown-menu"); // Click on two await click(parent.el, ".two button"); - assert.containsN(parent.el, "ul.o_dropdown_menu", 2); + assert.containsN(parent.el, ".dropdown-menu", 2); // Click on one again await click(parent.el, ".one button"); - assert.containsOnce(parent.el, "ul.o_dropdown_menu"); - assert.containsNone(parent.el.querySelector(".one"), "ul.o_dropdown_menu"); + assert.containsOnce(parent.el, ".dropdown-menu"); + assert.containsNone(parent.el.querySelector(".one"), ".dropdown-menu"); // Hover on one const one = parent.el.querySelector(".one"); one.querySelector("button").dispatchEvent(new MouseEvent("mouseenter")); await nextTick(); - assert.containsOnce(parent.el, "ul.o_dropdown_menu"); - assert.containsNone(parent.el.querySelector(".one"), "ul.o_dropdown_menu"); + assert.containsOnce(parent.el, ".dropdown-menu"); + assert.containsNone(parent.el.querySelector(".one"), ".dropdown-menu"); // Click outside await click(parent.el, "div.outside"); - assert.containsOnce(parent.el, "ul.o_dropdown_menu"); + assert.containsOnce(parent.el, ".dropdown-menu"); }); QUnit.test("dropdowns keynav", async (assert) => { @@ -547,20 +573,20 @@ QUnit.module("Components", ({ beforeEach }) => { `; env = await makeTestEnv(); parent = await mount(Parent, { env, target }); - assert.containsNone(parent.el, ".o_dropdown_menu", "menu is closed at start"); + assert.containsNone(parent.el, ".dropdown-menu", "menu is closed at start"); triggerHotkey("m", true); await nextTick(); assert.containsOnce( parent.el, - ".o_dropdown_menu", + ".dropdown-menu", "menu is opened after pressing the toggler hotkey" ); // Navigate with arrows assert.containsNone( parent.el, - ".o_dropdown_menu > .focus", + ".dropdown-menu > .focus", "menu should not have any active items" ); @@ -585,41 +611,41 @@ QUnit.module("Components", ({ beforeEach }) => { for (const step of scenarioSteps) { triggerHotkey(step.hotkey); await nextTick(); - assert.hasClass(parent.el.querySelector(".o_dropdown_menu > .focus"), step.expected); + assert.hasClass(parent.el.querySelector(".dropdown-menu > .focus"), step.expected); } // Select last one activated in previous scenario (item1) triggerHotkey("enter"); await nextTick(); - assert.containsNone(parent.el, ".o_dropdown_menu", "menu is closed after item selection"); + assert.containsNone(parent.el, ".dropdown-menu", "menu is closed after item selection"); // Reopen dropdown triggerHotkey("m", true); await nextTick(); assert.containsOnce( parent.el, - ".o_dropdown_menu", + ".dropdown-menu", "menu is opened after pressing the toggler hotkey" ); // Select second item through data-hotkey attribute triggerHotkey("2", true); await nextTick(); - assert.containsNone(parent.el, ".o_dropdown_menu", "menu is closed after item selection"); + assert.containsNone(parent.el, ".dropdown-menu", "menu is closed after item selection"); // Reopen dropdown triggerHotkey("m", true); await nextTick(); assert.containsOnce( parent.el, - ".o_dropdown_menu", + ".dropdown-menu", "menu is opened after pressing the toggler hotkey" ); // Close dropdown with keynav triggerHotkey("escape"); await nextTick(); - assert.containsNone(parent.el, ".o_dropdown_menu", "menu is closed after item selection"); + assert.containsNone(parent.el, ".dropdown-menu", "menu is closed after item selection"); assert.verifySteps(["1", "2"], "items should have been selected in this order"); }); @@ -639,16 +665,16 @@ QUnit.module("Components", ({ beforeEach }) => { env = await makeTestEnv(); parent = await mount(Parent, { env, target }); - assert.containsOnce(parent, ".o_dropdown"); - assert.containsNone(parent, ".o_dropdown .o_dropdown_menu"); - assert.containsNone(parent, ".o_dropdown button.o_dropdown_toggler"); + assert.containsOnce(parent, ".dropdown"); + assert.containsNone(parent, ".dropdown .dropdown-menu"); + assert.containsNone(parent, ".dropdown button.dropdown-toggle"); await click(parent.el, ".my_custom_toggler"); - assert.containsOnce(parent, ".o_dropdown .o_dropdown_menu"); - assert.containsN(parent, ".o_dropdown .o_dropdown_menu .o_dropdown_item", 2); + assert.containsOnce(parent, ".dropdown .dropdown-menu"); + assert.containsN(parent, ".dropdown .dropdown-menu .dropdown-item", 2); }); QUnit.test("multi-level dropdown: keynav", async (assert) => { - assert.expect(126); + assert.expect(125); class Parent extends owl.Component { onItemSelected(ev) { const { payload } = ev.detail; @@ -657,51 +683,21 @@ QUnit.module("Components", ({ beforeEach }) => { } Parent.template = owl.tags.xml` - O - - O - - O - O + O + + O + + O + O - O + O - O + O `; env = await makeTestEnv(); parent = await mount(Parent, { env, target }); - assert.containsNone(parent.el, ".o_dropdown_menu", "menus are closed at start"); - - // Open through hotkeys - triggerHotkey("alt+2"); - await nextTick(); - assert.containsNone(parent.el, ".o_dropdown_menu", "no menu is opened"); - - triggerHotkey("alt+1"); - await nextTick(); - assert.containsOnce(parent.el, ".o_dropdown_menu", "first menu is opened"); - - triggerHotkey("alt+2"); - await nextTick(); - assert.containsN(parent.el, ".o_dropdown_menu", 2, "second menu is opened"); - - triggerHotkey("alt+3"); - await nextTick(); - assert.containsN(parent.el, ".o_dropdown_menu", 3, "both menus are opened"); - - // Close through hotkeys - triggerHotkey("escape"); - await nextTick(); - assert.containsN(parent.el, ".o_dropdown_menu", 2, "third menu is closed"); - - triggerHotkey("escape"); - await nextTick(); - assert.containsOnce(parent.el, ".o_dropdown_menu", "second menu is closed"); - - triggerHotkey("escape"); - await nextTick(); - assert.containsNone(parent.el, ".o_dropdown_menu", "both menus are closed"); + assert.containsNone(parent.el, ".dropdown-menu", "menus are closed at start"); // Highlighting and selecting items const scenarioSteps = [ @@ -743,15 +739,15 @@ QUnit.module("Components", ({ beforeEach }) => { { hotkey: "enter", highlighted: ["second", "third", "third-first"] }, { hotkey: "enter", selected: "third-first" }, { hotkey: "alt+1" }, - { hotkey: "alt+e", selected: false }, - { hotkey: "alt+f", selected: "first-last" }, - { hotkey: "alt+1" }, - { hotkey: "alt+2", highlighted: ["second", "second-first"] }, - { hotkey: "alt+3", highlighted: ["second", "third", "third-first"] }, - { hotkey: "alt+d", selected: "third-last" }, - { hotkey: "alt+1" }, - { hotkey: "alt+2", highlighted: ["second", "second-first"] }, - { hotkey: "alt+e", selected: "second-last" }, + { hotkey: "arrowup", highlighted: ["first-last"] }, + { hotkey: "arrowup", highlighted: ["second"] }, + { hotkey: "arrowright", highlighted: ["second", "second-first"] }, + { hotkey: "arrowup", highlighted: ["second", "second-last"] }, + { hotkey: "arrowup", highlighted: ["second", "third"] }, + { hotkey: "arrowright", highlighted: ["second", "third", "third-first"] }, + { hotkey: "escape", highlighted: ["second", "third"] }, + { hotkey: "escape", highlighted: ["second"] }, + { hotkey: "escape", highlighted: [] }, ]; for (const [stepIndex, step] of scenarioSteps.entries()) { @@ -759,7 +755,9 @@ QUnit.module("Components", ({ beforeEach }) => { await nextTick(); if (step.highlighted !== undefined) { let index = 0; - const activeElements = parent.el.querySelectorAll(".focus"); + const activeElements = [...parent.el.querySelectorAll(".focus")].map((el) => + el.classList.contains("dropdown-toggle") ? el.parentElement : el + ); assert.ok( activeElements.length === step.highlighted.length, `step ${stepIndex}: all active elements to check are found` @@ -781,7 +779,7 @@ QUnit.module("Components", ({ beforeEach }) => { Parent.template = owl.tags.xml` O - + O @@ -789,7 +787,7 @@ QUnit.module("Components", ({ beforeEach }) => { serviceRegistry.add("localization", makeFakeLocalizationService({ direction: "rtl" })); env = await makeTestEnv(); parent = await mount(Parent, { env, target }); - assert.containsNone(parent.el, ".o_dropdown_menu", "menus are closed at start"); + assert.containsNone(parent.el, ".dropdown-menu", "menus are closed at start"); // Highlighting and selecting items const scenarioSteps = [ @@ -805,7 +803,9 @@ QUnit.module("Components", ({ beforeEach }) => { await nextTick(); if (step.highlighted !== undefined) { let index = 0; - const activeElements = parent.el.querySelectorAll(".focus"); + const activeElements = [...parent.el.querySelectorAll(".focus")].map((el) => + el.classList.contains("dropdown-toggle") ? el.parentElement : el + ); assert.ok( activeElements.length === step.highlighted.length, `step ${stepIndex}: all active elements to check are found` @@ -817,6 +817,35 @@ QUnit.module("Components", ({ beforeEach }) => { } }); + QUnit.test( + "multi-level dropdown: mouseentering a dropdown item should close any subdropdown", + async (assert) => { + assert.expect(4); + class Parent extends owl.Component {} + Parent.template = owl.tags.xml` + + + + + `; + env = await makeTestEnv(); + parent = await mount(Parent, { env, target }); + assert.containsNone(parent.el, ".dropdown-menu", "menus are closed at start"); + + // Open main dropdown + await click(parent.el, ".main"); + assert.containsOnce(parent.el, ".dropdown-menu", "1st menu is opened"); + + // Mouse enter sub dropdown + await mouseEnter(parent.el, ".sub"); + assert.containsN(parent.el, ".dropdown-menu", 2, "all menus are opened"); + + // Mouse enter the adjacent dropdown item + await mouseEnter(parent.el, ".item"); + assert.containsOnce(parent.el, ".dropdown-menu", "only 1st menu is opened"); + } + ); + QUnit.test("multi-level dropdown: unsubscribe all keynav when root close", async (assert) => { assert.expect(14); class Parent extends owl.Component {} @@ -840,35 +869,41 @@ QUnit.module("Components", ({ beforeEach }) => { }, }); parent = await mount(Parent, { env, target }); - assert.containsNone(parent.el, ".o_dropdown_menu", "menus are closed at start"); + assert.containsNone(parent.el, ".dropdown-menu", "menus are closed at start"); assert.strictEqual(hotkeyRegistrationsCount, 0, "no hotkey registered"); // Open dropdowns one by one await click(parent.el, ".first"); - assert.containsOnce(parent.el, ".o_dropdown_menu", "1st menu is opened"); + assert.containsOnce(parent.el, ".dropdown-menu", "1st menu is opened"); assert.strictEqual(hotkeyRegistrationsCount, 10, "1st menu hotkeys registered"); - await click(parent.el, ".second"); - assert.containsN(parent.el, ".o_dropdown_menu", 2, "2nd menu is also opened"); + await mouseEnter(parent.el, ".second"); + assert.containsN(parent.el, ".dropdown-menu", 2, "2nd menu is also opened"); assert.strictEqual(hotkeyRegistrationsCount, 20, "2nd menu hotkeys also registered"); - await click(parent.el, ".third"); - assert.containsN(parent.el, ".o_dropdown_menu", 3, "3rd menu is also opened"); + await mouseEnter(parent.el, ".third"); + assert.containsN(parent.el, ".dropdown-menu", 3, "3rd menu is also opened"); assert.strictEqual(hotkeyRegistrationsCount, 30, "3rd menu hotkeys also registered"); - // Close second - await click(parent.el, ".second"); - assert.containsN(parent.el, ".o_dropdown_menu", 1, "only 1st menu stay opened"); - assert.strictEqual(hotkeyRegistrationsCount, 10, "only 1st menu hotkeys registered"); + // Close third + triggerHotkey("escape"); + await nextTick(); + assert.containsN(parent.el, ".dropdown-menu", 2, "two menus still opened"); + assert.strictEqual(hotkeyRegistrationsCount, 20, "two menus hotkeys registered"); // Reopen second - await click(parent.el, ".second"); - assert.containsN(parent.el, ".o_dropdown_menu", 2, "2nd menu is also opened"); - assert.strictEqual(hotkeyRegistrationsCount, 20, "2nd menu hotkeys also registered"); + await mouseEnter(parent.el, ".third"); + assert.containsN(parent.el, ".dropdown-menu", 3, "3rd menu is also opened"); + assert.strictEqual(hotkeyRegistrationsCount, 30, "3rd menu hotkeys also registered"); - // Close first - await click(parent.el, ".first"); - assert.containsNone(parent.el, ".o_dropdown_menu", "all menus are now closed"); + // Close third, second and first + triggerHotkey("escape"); + await nextTick(); + triggerHotkey("escape"); + await nextTick(); + triggerHotkey("escape"); + await nextTick(); + assert.containsNone(parent.el, ".dropdown-menu", "all menus are now closed"); assert.strictEqual(hotkeyRegistrationsCount, 0, "no hotkey registration left"); }); }); diff --git a/addons/web/static/tests/helpers/utils.js b/addons/web/static/tests/helpers/utils.js index c128add7b7d..204b4c9b30e 100644 --- a/addons/web/static/tests/helpers/utils.js +++ b/addons/web/static/tests/helpers/utils.js @@ -228,6 +228,10 @@ export function click(el, selector) { return triggerEvent(el, selector, "click", { bubbles: true, cancelable: true }); } +export async function mouseEnter(el, selector) { + return triggerEvent(el, selector, "mouseenter"); +} + /** * Triggers an hotkey properly disregarding the operating system. * diff --git a/addons/web/static/tests/legacy/components/action_menus_tests.js b/addons/web/static/tests/legacy/components/action_menus_tests.js index 2cc2f46f5db..ab2aac9be9c 100644 --- a/addons/web/static/tests/legacy/components/action_menus_tests.js +++ b/addons/web/static/tests/legacy/components/action_menus_tests.js @@ -56,16 +56,16 @@ odoo.define('web.action_menus_tests', function (require) { props: this.props, }); - const dropdowns = actionMenus.el.getElementsByClassName('o_dropdown'); + const dropdowns = actionMenus.el.getElementsByClassName('dropdown'); assert.strictEqual(dropdowns.length, 2, "ActionMenus should contain 2 menus"); assert.strictEqual(dropdowns[0].querySelector('.o_dropdown_title').innerText.trim(), "Print"); assert.strictEqual(dropdowns[1].querySelector('.o_dropdown_title').innerText.trim(), "Action"); - assert.containsNone(actionMenus, '.o_dropdown_menu'); + assert.containsNone(actionMenus, '.dropdown-menu'); await testUtils.controlPanel.toggleActionMenu(actionMenus, "Action"); - assert.containsOnce(actionMenus, '.o_dropdown_menu'); - assert.containsN(actionMenus, '.o_dropdown_menu .o_menu_item', 4); + assert.containsOnce(actionMenus, '.dropdown-menu'); + assert.containsN(actionMenus, '.dropdown-menu .o_menu_item', 4); const actionsTexts = [...dropdowns[1].querySelectorAll('.o_menu_item')].map(el => el.innerText.trim()); assert.deepEqual(actionsTexts, [ "Boil'em", @@ -76,12 +76,12 @@ odoo.define('web.action_menus_tests', function (require) { await testUtils.controlPanel.toggleActionMenu(actionMenus, "Print"); - assert.containsOnce(actionMenus, '.o_dropdown_menu'); - assert.containsN(actionMenus, '.o_dropdown_menu .o_menu_item', 1); + assert.containsOnce(actionMenus, '.dropdown-menu'); + assert.containsN(actionMenus, '.dropdown-menu .o_menu_item', 1); await testUtils.controlPanel.toggleActionMenu(actionMenus, "Print"); - assert.containsNone(actionMenus, '.o_dropdown_menu'); + assert.containsNone(actionMenus, '.dropdown-menu'); actionMenus.destroy(); }); diff --git a/addons/web/static/tests/legacy/components/dropdown_menu_tests.js b/addons/web/static/tests/legacy/components/dropdown_menu_tests.js index 3aff0ae14ea..af65c970eca 100644 --- a/addons/web/static/tests/legacy/components/dropdown_menu_tests.js +++ b/addons/web/static/tests/legacy/components/dropdown_menu_tests.js @@ -42,7 +42,7 @@ odoo.define('web.dropdown_menu_tests', function (require) { }); assert.strictEqual(dropdown.el.querySelector('button').innerText.trim(), "Dropdown"); - assert.containsNone(dropdown, 'ul.o_dropdown_menu'); + assert.containsNone(dropdown, '.dropdown-menu'); await testUtils.dom.click(dropdown.el.querySelector('button')); @@ -57,12 +57,12 @@ odoo.define('web.dropdown_menu_tests', function (require) { for (const dropdownEl of dropdownElements) { await testUtils.dom.click(dropdownEl); } - assert.containsOnce(dropdown, 'ul.o_dropdown_menu', + assert.containsOnce(dropdown, '.dropdown-menu', "Clicking on any item of the dropdown should not close it"); await testUtils.dom.click(document.body); - assert.containsNone(dropdown, 'ul.o_dropdown_menu', + assert.containsNone(dropdown, '.dropdown-menu', "Clicking outside of the dropdown should close it"); dropdown.destroy(); @@ -349,7 +349,7 @@ odoo.define('web.dropdown_menu_tests', function (require) { await navigate('Escape', true); // Close the dropdown - assert.containsNone(dropdown, 'ul.o_dropdown_menu', "Dropdown should be folded"); + assert.containsNone(dropdown, '.dropdown-menu', "Dropdown should be folded"); dropdown.destroy(); }); @@ -373,25 +373,25 @@ odoo.define('web.dropdown_menu_tests', function (require) { const parent = new Parent(); await parent.mount(testUtils.prepareTarget(), { position: 'first-child' }); - const [menu1, menu2] = parent.el.querySelectorAll('.o_dropdown'); + const [menu1, menu2] = parent.el.querySelectorAll('.dropdown'); - assert.containsNone(parent, '.o_dropdown_menu'); + assert.containsNone(parent, '.dropdown-menu'); await testUtils.dom.click(menu1.querySelector('button')); - assert.containsOnce(parent, '.o_dropdown_menu'); - assert.containsOnce(parent, '.o_dropdown.first .o_dropdown_menu'); + assert.containsOnce(parent, '.dropdown-menu'); + assert.containsOnce(parent, '.dropdown.first .dropdown-menu'); await testUtils.dom.click(menu2.querySelector('button')); - assert.containsOnce(parent, '.o_dropdown_menu'); - assert.containsOnce(parent, '.o_dropdown.second .o_dropdown_menu'); + assert.containsOnce(parent, '.dropdown-menu'); + assert.containsOnce(parent, '.dropdown.second .dropdown-menu'); await testUtils.dom.click(menu2.querySelector('.o_menu_item a')); await testUtils.dom.click(menu1.querySelector('button')); - assert.containsOnce(parent, '.o_dropdown_menu'); - assert.containsOnce(parent, '.o_dropdown.first .o_dropdown_menu'); + assert.containsOnce(parent, '.dropdown-menu'); + assert.containsOnce(parent, '.dropdown.first .dropdown-menu'); parent.destroy(); }); @@ -419,7 +419,7 @@ odoo.define('web.dropdown_menu_tests', function (require) { const parent = new Parent(); await parent.mount(testUtils.prepareTarget(), { position: "first-child" }); - const menu = parent.el.querySelector(".o_dropdown"); + const menu = parent.el.querySelector(".dropdown"); assert.doesNotHaveClass(menu, "show", "dropdown should not be open"); await testUtils.dom.click(menu.querySelector("button")); diff --git a/addons/web/static/tests/legacy/control_panel/comparison_menu_tests.js b/addons/web/static/tests/legacy/control_panel/comparison_menu_tests.js index 82d4fb2128c..ab697122950 100644 --- a/addons/web/static/tests/legacy/control_panel/comparison_menu_tests.js +++ b/addons/web/static/tests/legacy/control_panel/comparison_menu_tests.js @@ -1,6 +1,8 @@ odoo.define('web.comparison_menu_tests', function (require) { "use strict"; + const { browser } = require('@web/core/browser/browser'); + const { patchWithCleanup } = require('@web/../tests/helpers/utils'); const cpHelpers = require('@web/../tests/search/helpers'); const { createControlPanel, @@ -27,6 +29,9 @@ odoo.define('web.comparison_menu_tests', function (require) { fields: this.fields, searchMenuTypes, }; + patchWithCleanup(browser, { + setTimeout: (fn) => fn(), + }); }, }, function () { @@ -42,8 +47,8 @@ odoo.define('web.comparison_menu_tests', function (require) { }; const controlPanel = await createControlPanel(params); - assert.containsOnce(controlPanel, ".o_dropdown.o_filter_menu"); - assert.containsNone(controlPanel, ".o_dropdown.o_comparison_menu"); + assert.containsOnce(controlPanel, ".dropdown.o_filter_menu"); + assert.containsNone(controlPanel, ".dropdown.o_comparison_menu"); await cpHelpers.toggleFilterMenu(controlPanel); await cpHelpers.toggleMenuItem(controlPanel, "Birthday"); @@ -55,7 +60,7 @@ odoo.define('web.comparison_menu_tests', function (require) { await cpHelpers.toggleComparisonMenu(controlPanel); const comparisonOptions = [...controlPanel.el.querySelectorAll( - '.o_comparison_menu li' + '.o_comparison_menu .o_menu_item' )]; assert.strictEqual(comparisonOptions.length, 2); assert.deepEqual( diff --git a/addons/web/static/tests/legacy/control_panel/custom_filter_item_tests.js b/addons/web/static/tests/legacy/control_panel/custom_filter_item_tests.js index 95b8af4338f..9f715adc989 100644 --- a/addons/web/static/tests/legacy/control_panel/custom_filter_item_tests.js +++ b/addons/web/static/tests/legacy/control_panel/custom_filter_item_tests.js @@ -10,6 +10,10 @@ odoo.define('web.filter_menu_generator_tests', function (require) { const cpHelpers = require('@web/../tests/search/helpers'); const { createComponent } = testUtils; + const toggleAddCustomFilterStandalone = async (el) => { + await cpHelpers.toggleMenu(el, "Add Custom Filter"); + }; + QUnit.module('Components', { beforeEach: function () { this.fields = { @@ -42,7 +46,7 @@ odoo.define('web.filter_menu_generator_tests', function (require) { assert.hasClass(cfi.el, 'o_add_custom_filter_menu'); assert.strictEqual(cfi.el.children.length, 1); - await cpHelpers.toggleAddCustomFilter(cfi); + await toggleAddCustomFilterStandalone(cfi); // Single condition assert.containsOnce(cfi, '.o_filter_condition'); @@ -82,7 +86,7 @@ odoo.define('web.filter_menu_generator_tests', function (require) { }); // Open custom filter form - await cpHelpers.toggleAddCustomFilter(cfi); + await toggleAddCustomFilterStandalone(cfi); // Retrieve second selectable values for field and operator dropdowns const fieldSecondValue = cfi.el.querySelector('.o_generator_menu_field option:nth-of-type(2)').value; @@ -137,7 +141,7 @@ odoo.define('web.filter_menu_generator_tests', function (require) { domain: '[["binary_field","!=",False]]', type: 'filter', }]; - await cpHelpers.toggleAddCustomFilter(cfi); + await toggleAddCustomFilterStandalone(cfi); await testUtils.fields.editSelect(cfi.el.querySelector('.o_generator_menu_field'), 'binary_field'); await cpHelpers.applyFilter(cfi); @@ -147,7 +151,6 @@ odoo.define('web.filter_menu_generator_tests', function (require) { domain: '[["binary_field","=",False]]', type: 'filter', }]; - await cpHelpers.toggleAddCustomFilter(cfi); await testUtils.fields.editSelect(cfi.el.querySelector('.o_generator_menu_field'), 'binary_field'); await cpHelpers.editConditionOperator(cfi, 0, '='); await cpHelpers.applyFilter(cfi); @@ -180,7 +183,7 @@ odoo.define('web.filter_menu_generator_tests', function (require) { domain: '[["color","=","black"]]', type: 'filter', }]; - await cpHelpers.toggleAddCustomFilter(cfi); + await toggleAddCustomFilterStandalone(cfi); await testUtils.fields.editSelect(cfi.el.querySelector('.o_generator_menu_field'), 'color'); await cpHelpers.applyFilter(cfi); @@ -190,7 +193,6 @@ odoo.define('web.filter_menu_generator_tests', function (require) { domain: '[["color","=","white"]]', type: 'filter', }]; - await cpHelpers.toggleAddCustomFilter(cfi); await testUtils.fields.editSelect(cfi.el.querySelector('.o_generator_menu_field'), 'color'); await testUtils.fields.editSelect(cfi.el.querySelector('.o_generator_menu_value select'), 'white'); await cpHelpers.applyFilter(cfi); @@ -199,7 +201,7 @@ odoo.define('web.filter_menu_generator_tests', function (require) { }); QUnit.test('adding a simple filter works', async function (assert) { - assert.expect(6); + assert.expect(7); delete this.fields.date_field; @@ -221,13 +223,14 @@ odoo.define('web.filter_menu_generator_tests', function (require) { env: { searchModel }, }); - await cpHelpers.toggleAddCustomFilter(cfi); + await toggleAddCustomFilterStandalone(cfi); await testUtils.fields.editSelect(cfi.el.querySelector('.o_generator_menu_field'), 'boolean_field'); await cpHelpers.applyFilter(cfi); - // The only thing visible should be the button 'Add Custome Filter'; - assert.strictEqual(cfi.el.children.length, 1); - assert.containsOnce(cfi, 'button.o_dropdown_toggler'); + // The only things visible should be the button 'Add Custom Filter' and the menu; + assert.strictEqual(cfi.el.children.length, 2); + assert.containsOnce(cfi, 'button.dropdown-toggle'); + assert.containsOnce(cfi, '.dropdown-menu'); cfi.destroy(); }); @@ -262,7 +265,6 @@ odoo.define('web.filter_menu_generator_tests', function (require) { async function testValue(operator, value) { // open filter menu generator, select ID field, switch operator, type value, then click apply - await cpHelpers.toggleAddCustomFilter(cfi); await cpHelpers.editConditionField(cfi, 0, 'id_field'); await cpHelpers.editConditionOperator(cfi, 0, operator); await cpHelpers.editConditionValue(cfi, 0, @@ -271,6 +273,7 @@ odoo.define('web.filter_menu_generator_tests', function (require) { await cpHelpers.applyFilter(cfi); } + await toggleAddCustomFilterStandalone(cfi); for (const domain of [...expectedDomains]) { await testValue(domain[0][1], domain[0][2]); } @@ -314,7 +317,6 @@ odoo.define('web.filter_menu_generator_tests', function (require) { async function testValue(value) { // open filter menu generator, select trululu field and enter string `a`, then click apply - await cpHelpers.toggleAddCustomFilter(cfi); await cpHelpers.editConditionField(cfi, 0, 'many2one_field'); await cpHelpers.editConditionValue(cfi, 0, value @@ -322,6 +324,7 @@ odoo.define('web.filter_menu_generator_tests', function (require) { await cpHelpers.applyFilter(cfi); } + await toggleAddCustomFilterStandalone(cfi); for (const value of testedValues) { await testValue(value); } @@ -359,7 +362,7 @@ odoo.define('web.filter_menu_generator_tests', function (require) { env: { searchModel }, }); - await cpHelpers.toggleAddCustomFilter(cfi); + await toggleAddCustomFilterStandalone(cfi); await testUtils.fields.editSelect(cfi.el.querySelector('.o_generator_menu_field'), 'date_time_field'); assert.strictEqual(cfi.el.querySelector('.o_generator_menu_field').value, 'date_time_field'); @@ -402,7 +405,7 @@ odoo.define('web.filter_menu_generator_tests', function (require) { env: { searchModel }, }); - await cpHelpers.toggleAddCustomFilter(cfi); + await toggleAddCustomFilterStandalone(cfi); await testUtils.fields.editSelect(cfi.el.querySelector('.o_generator_menu_field'), 'date_time_field'); assert.strictEqual(cfi.el.querySelector('.o_generator_menu_field').value, 'date_time_field'); @@ -428,7 +431,7 @@ odoo.define('web.filter_menu_generator_tests', function (require) { }, }); - await cpHelpers.toggleAddCustomFilter(cfi); + await toggleAddCustomFilterStandalone(cfi); await cpHelpers.addCondition(cfi); await cpHelpers.editConditionField(cfi, 0, "float_field"); @@ -477,7 +480,7 @@ odoo.define('web.filter_menu_generator_tests', function (require) { }, }); - await cpHelpers.toggleAddCustomFilter(cfi); + await toggleAddCustomFilterStandalone(cfi); await cpHelpers.addCondition(cfi); await cpHelpers.editConditionField(cfi, 0, "float_field"); @@ -542,7 +545,7 @@ odoo.define('web.filter_menu_generator_tests', function (require) { env: { searchModel }, }); - await cpHelpers.toggleAddCustomFilter(cfi); + await toggleAddCustomFilterStandalone(cfi); await cpHelpers.addCondition(cfi); await cpHelpers.addCondition(cfi); await cpHelpers.addCondition(cfi); diff --git a/addons/web/static/tests/legacy/control_panel/custom_group_by_item_tests.js b/addons/web/static/tests/legacy/control_panel/custom_group_by_item_tests.js index 7fbca38fcde..baed11f3cdc 100644 --- a/addons/web/static/tests/legacy/control_panel/custom_group_by_item_tests.js +++ b/addons/web/static/tests/legacy/control_panel/custom_group_by_item_tests.js @@ -29,21 +29,21 @@ odoo.define('web.groupby_menu_generator_tests', function (require) { assert.hasClass(cgi.el, 'o_add_custom_group_menu'); assert.strictEqual(cgi.el.children.length, 1); - await testUtils.dom.click(cgi.el.querySelector('.o_add_custom_group_menu .o_dropdown_toggler')); + await testUtils.dom.click(cgi.el.querySelector('.o_add_custom_group_menu .dropdown-toggle')); // Single select node with a single option - assert.containsOnce(cgi, 'li > select'); - assert.strictEqual(cgi.el.querySelector('li > select option').innerText.trim(), + assert.containsOnce(cgi, 'div > select'); + assert.strictEqual(cgi.el.querySelector('div > select option').innerText.trim(), "Super Date"); // Button apply - assert.containsOnce(cgi, 'li > button.btn.btn-primary'); + assert.containsOnce(cgi, 'div > button.btn.btn-primary'); cgi.destroy(); }); QUnit.test('select a field name in Add Custom Group menu properly trigger the corresponding field', async function (assert) { - assert.expect(4); + assert.expect(5); const fields = [ { sortable: true, name: 'candlelight', string: 'Candlelight', type: 'boolean' }, @@ -61,12 +61,13 @@ odoo.define('web.groupby_menu_generator_tests', function (require) { env: { searchModel }, }); - await testUtils.dom.click(cgi.el.querySelector('.o_add_custom_group_menu .o_dropdown_toggler')); - await testUtils.dom.click(cgi.el.querySelector('li > button.btn.btn-primary')); + await testUtils.dom.click(cgi.el.querySelector('.o_add_custom_group_menu .dropdown-toggle')); + await testUtils.dom.click(cgi.el.querySelector('div > button.btn.btn-primary')); - // The only thing visible should be the button 'Add Custome Group'; - assert.strictEqual(cgi.el.children.length, 1); - assert.containsOnce(cgi, '.o_dropdown_toggler'); + // The only things visible should be the button 'Add Custome Group' and the dropdown menu; + assert.strictEqual(cgi.el.children.length, 2); + assert.containsOnce(cgi, '.dropdown-toggle'); + assert.containsOnce(cgi, '.dropdown-menu'); cgi.destroy(); }); diff --git a/addons/web/static/tests/legacy/control_panel/favorite_menu_tests.js b/addons/web/static/tests/legacy/control_panel/favorite_menu_tests.js index f047a98ee61..7434c619cda 100644 --- a/addons/web/static/tests/legacy/control_panel/favorite_menu_tests.js +++ b/addons/web/static/tests/legacy/control_panel/favorite_menu_tests.js @@ -1,6 +1,9 @@ odoo.define('web.favorite_menu_tests', function (require) { "use strict"; + const { browser } = require('@web/core/browser/browser'); + const { patchWithCleanup } = require('@web/../tests/helpers/utils'); + const FormView = require('web.FormView'); const testUtils = require('web.test_utils'); @@ -19,6 +22,9 @@ odoo.define('web.favorite_menu_tests', function (require) { float_field: { string: "Float", type: "float", group_operator: 'sum' }, foo: { string: "Foo", type: "char", store: true, sortable: true }, }; + patchWithCleanup(browser, { + setTimeout: (fn) => fn(), + }); }, }, function () { @@ -39,7 +45,7 @@ odoo.define('web.favorite_menu_tests', function (require) { await cpHelpers.toggleFavoriteMenu(controlPanel); assert.containsNone(controlPanel, '.dropdown-divider'); assert.containsOnce(controlPanel, '.o_add_favorite'); - assert.strictEqual(controlPanel.el.querySelector('.o_add_favorite > button span').innerText.trim(), + assert.strictEqual(controlPanel.el.querySelector('.o_add_favorite > button').innerText.trim(), "Save current search"); await cpHelpers.toggleSaveFavorite(controlPanel); @@ -288,7 +294,7 @@ odoo.define('web.favorite_menu_tests', function (require) { // confirm deletion await testUtils.dom.click(document.querySelector('div.o_dialog footer button')); assert.deepEqual(cpHelpers.getFacetTexts(controlPanel), []); - const itemEls = controlPanel.el.querySelectorAll('.o_favorite_menu .o_dropdown_item'); + const itemEls = controlPanel.el.querySelectorAll('.o_favorite_menu .dropdown-item'); assert.deepEqual([...itemEls].map(e => e.innerText.trim()), ["Save current search"]); controlPanel.destroy(); diff --git a/addons/web/static/tests/legacy/control_panel/filter_menu_tests.js b/addons/web/static/tests/legacy/control_panel/filter_menu_tests.js index e1f52fbef5d..1fa900093e6 100644 --- a/addons/web/static/tests/legacy/control_panel/filter_menu_tests.js +++ b/addons/web/static/tests/legacy/control_panel/filter_menu_tests.js @@ -1,6 +1,8 @@ odoo.define('web.filter_menu_tests', function (require) { "use strict"; + const { browser } = require('@web/core/browser/browser'); + const { patchWithCleanup } = require('@web/../tests/helpers/utils'); const testUtils = require('web.test_utils'); const cpHelpers = require('@web/../tests/search/helpers'); @@ -15,6 +17,9 @@ odoo.define('web.filter_menu_tests', function (require) { date_field: { string: "Date", type: "date", store: true, sortable: true, searchable: true }, foo: { string: "Foo", type: "char", store: true, sortable: true }, }; + patchWithCleanup(browser, { + setTimeout: (fn) => fn(), + }); }, }, function () { @@ -52,7 +57,7 @@ odoo.define('web.filter_menu_tests', function (require) { await cpHelpers.toggleFilterMenu(controlPanel); assert.containsOnce(controlPanel, '.o_menu_item'); assert.containsOnce(controlPanel, '.dropdown-divider'); - assert.containsOnce(controlPanel, 'li.o_add_custom_filter_menu'); + assert.containsOnce(controlPanel, 'div.o_add_custom_filter_menu'); controlPanel.destroy(); }); @@ -225,7 +230,7 @@ odoo.define('web.filter_menu_tests', function (require) { await cpHelpers.toggleFilterMenu(controlPanel); await cpHelpers.toggleMenuItem(controlPanel, "Date"); - const optionEls = controlPanel.el.querySelectorAll('li.o_item_option'); + const optionEls = controlPanel.el.querySelectorAll('span.o_item_option'); // default filter should be activated with the global default period 'this_month' const { domain } = controlPanel.getQuery(); diff --git a/addons/web/static/tests/legacy/control_panel/groupby_menu_tests.js b/addons/web/static/tests/legacy/control_panel/groupby_menu_tests.js index a3d2e638923..648a84b7a14 100644 --- a/addons/web/static/tests/legacy/control_panel/groupby_menu_tests.js +++ b/addons/web/static/tests/legacy/control_panel/groupby_menu_tests.js @@ -1,6 +1,8 @@ odoo.define('web.groupby_menu_tests', function (require) { "use strict"; + const { browser } = require('@web/core/browser/browser'); + const { patchWithCleanup } = require('@web/../tests/helpers/utils'); const testUtils = require('web.test_utils'); const cpHelpers = require('@web/../tests/search/helpers'); @@ -18,6 +20,9 @@ odoo.define('web.groupby_menu_tests', function (require) { foo: { string: "Foo", type: "char", store: true, sortable: true }, m2m: { string: "Many2Many", type: "many2many", store: true}, }; + patchWithCleanup(browser, { + setTimeout: (fn) => fn(), + }); }, }, function () { @@ -210,7 +215,7 @@ odoo.define('web.groupby_menu_tests', function (require) { await cpHelpers.toggleGroupByMenu(controlPanel); await cpHelpers.toggleMenuItem(controlPanel, 0); - const optionEls = controlPanel.el.querySelectorAll('li.o_item_option'); + const optionEls = controlPanel.el.querySelectorAll('span.o_item_option'); // default groupby should be activated with the default inteval 'week' const { groupBy } = controlPanel.getQuery(); diff --git a/addons/web/static/tests/legacy/control_panel/search_bar_tests.js b/addons/web/static/tests/legacy/control_panel/search_bar_tests.js index cf4afa89cb5..65095939e3e 100644 --- a/addons/web/static/tests/legacy/control_panel/search_bar_tests.js +++ b/addons/web/static/tests/legacy/control_panel/search_bar_tests.js @@ -8,6 +8,8 @@ import Registry from "web.Registry"; import SearchBar from "web.SearchBar"; import { registry } from "@web/core/registry"; import * as cpHelpers from "@web/../tests/search/helpers"; +import { patchWithCleanup } from "@web/../tests/helpers/utils"; +import { browser } from "@web/core/browser/browser"; let serverData; QUnit.module("Search Bar (legacy)", (hooks) => { @@ -74,6 +76,7 @@ QUnit.module("Search Bar (legacy)", (hooks) => { QUnit.test("navigation with facets", async function (assert) { assert.expect(4); + patchWithCleanup(browser, { setTimeout: (fn) => fn() }); const webClient = await createWebClient({ serverData }); await doAction(webClient, 1); diff --git a/addons/web/static/tests/legacy/fields/relational_fields/field_many2one_tests.js b/addons/web/static/tests/legacy/fields/relational_fields/field_many2one_tests.js index 620c25d8c40..5ef7d10c595 100644 --- a/addons/web/static/tests/legacy/fields/relational_fields/field_many2one_tests.js +++ b/addons/web/static/tests/legacy/fields/relational_fields/field_many2one_tests.js @@ -10,6 +10,8 @@ var StandaloneFieldManagerMixin = require('web.StandaloneFieldManagerMixin'); var testUtils = require('web.test_utils'); var Widget = require('web.Widget'); +const { browser } = require('@web/core/browser/browser'); +const { patchWithCleanup } = require('@web/../tests/helpers/utils'); const cpHelpers = require('@web/../tests/search/helpers'); var createView = testUtils.createView; @@ -544,6 +546,12 @@ QUnit.module('fields', {}, function () { display_name: "Partner 9", }); this.data.partner.fields.datetime.searchable = true; + + // add custom filter needs this + patchWithCleanup(browser, { + setTimeout: (fn) => fn(), + }); + var form = await createView({ View: FormView, model: 'partner', @@ -3126,21 +3134,21 @@ QUnit.module('fields', {}, function () { let filterMenuCss = '.o_search_options > .o_filter_menu'; let groupByMenuCss = '.o_search_options > .o_group_by_menu'; - await testUtils.dom.click(document.querySelector(`${filterMenuCss} > .o_dropdown_toggler`)); + await testUtils.dom.click(document.querySelector(`${filterMenuCss} > .dropdown-toggle`)); assert.hasClass(document.querySelector(filterMenuCss), 'show'); - assert.isVisible(document.querySelector(`${filterMenuCss} > .o_dropdown_menu`), + assert.isVisible(document.querySelector(`${filterMenuCss} > .dropdown-menu`), "the filter dropdown menu should be visible"); assert.doesNotHaveClass(document.querySelector(groupByMenuCss), 'show'); - assert.isNotVisible(document.querySelector(`${groupByMenuCss} > .o_dropdown_menu`), + assert.isNotVisible(document.querySelector(`${groupByMenuCss} > .dropdown-menu`), "the Group by dropdown menu should be not visible"); - await testUtils.dom.click(document.querySelector(`${groupByMenuCss} > .o_dropdown_toggler`)); + await testUtils.dom.click(document.querySelector(`${groupByMenuCss} > .dropdown-toggle`)); assert.hasClass(document.querySelector(groupByMenuCss), 'show'); - assert.isVisible(document.querySelector(`${groupByMenuCss} > .o_dropdown_menu`), + assert.isVisible(document.querySelector(`${groupByMenuCss} > .dropdown-menu`), "the group by dropdown menu should be visible"); assert.doesNotHaveClass(document.querySelector(filterMenuCss), 'show'); - assert.isNotVisible(document.querySelector(`${filterMenuCss} > .o_dropdown_menu`), + assert.isNotVisible(document.querySelector(`${filterMenuCss} > .dropdown-menu`), "the filter dropdown menu should be not visible"); $fakeDialog.remove(); diff --git a/addons/web/static/tests/legacy/helpers/test_utils_control_panel.js b/addons/web/static/tests/legacy/helpers/test_utils_control_panel.js index a4b6e2d47fa..f0b4391b478 100644 --- a/addons/web/static/tests/legacy/helpers/test_utils_control_panel.js +++ b/addons/web/static/tests/legacy/helpers/test_utils_control_panel.js @@ -14,7 +14,7 @@ odoo.define('web.test_utils_control_panel', function (require) { * @returns {Promise} */ async function toggleMenu(el, menuFinder) { - const menu = findItem(el, `.o_dropdown > button`, menuFinder); + const menu = findItem(el, `.dropdown > button`, menuFinder); await click(menu); } @@ -67,7 +67,7 @@ odoo.define('web.test_utils_control_panel', function (require) { * @returns {string[]} */ function getMenuItemTexts(el) { - return [...getNode(el).querySelectorAll(`.o_dropdown ul .o_menu_item`)].map( + return [...getNode(el).querySelectorAll(`.dropdown ul .o_menu_item`)].map( e => e.innerText.trim() ); } diff --git a/addons/web/static/tests/legacy/views/form_tests.js b/addons/web/static/tests/legacy/views/form_tests.js index c723a5db7a0..cf9b2b61b77 100644 --- a/addons/web/static/tests/legacy/views/form_tests.js +++ b/addons/web/static/tests/legacy/views/form_tests.js @@ -6252,8 +6252,8 @@ QUnit.module('Views', { }, }); - assert.containsNone(form, '.o_cp_action_menus .o_dropdown:contains(Print)'); - assert.containsOnce(form, '.o_cp_action_menus .o_dropdown:contains(Action)'); + assert.containsNone(form, '.o_cp_action_menus .dropdown:contains(Print)'); + assert.containsOnce(form, '.o_cp_action_menus .dropdown:contains(Action)'); await testUtils.controlPanel.toggleActionMenu(form); diff --git a/addons/web/static/tests/legacy/views/graph_tests.js b/addons/web/static/tests/legacy/views/graph_tests.js index 29312aa9b1e..321c4b30d17 100644 --- a/addons/web/static/tests/legacy/views/graph_tests.js +++ b/addons/web/static/tests/legacy/views/graph_tests.js @@ -5,7 +5,8 @@ const searchUtils = require('web.searchUtils'); const GraphView = require('web.GraphView'); const testUtils = require('web.test_utils'); const { sortBy } = require('web.utils'); -const { legacyExtraNextTick } = require("@web/../tests/helpers/utils"); +const { browser } = require('@web/core/browser/browser'); +const { legacyExtraNextTick, patchWithCleanup } = require("@web/../tests/helpers/utils"); const { createWebClient, doAction } = require('@web/../tests/webclient/helpers'); const legacyViewRegistry = require("web.view_registry"); const { registry } = require("@web/core/registry"); @@ -1615,6 +1616,8 @@ QUnit.module('Views', { this.unpatchDate = patchDate(2016, 11, 20, 1, 0, 0); + patchWithCleanup(browser, { setTimeout: (fn) => fn() }); + const graph = await createView({ View: GraphView, model: "foo", diff --git a/addons/web/static/tests/legacy/views/list_tests.js b/addons/web/static/tests/legacy/views/list_tests.js index bfa5a6eb3e9..ab1356f30a1 100644 --- a/addons/web/static/tests/legacy/views/list_tests.js +++ b/addons/web/static/tests/legacy/views/list_tests.js @@ -228,7 +228,7 @@ QUnit.module('Views', { assert.ok(list.$('tbody td.o_list_record_selector').length, 'should have at least one record'); await testUtils.dom.click(list.$('tbody td.o_list_record_selector:first input')); - assert.containsNone(list.el, 'div.o_control_panel .o_cp_action_menus .o_dropdown_menu'); + assert.containsNone(list.el, 'div.o_control_panel .o_cp_action_menus .dropdown-menu'); list.destroy(); }); @@ -1405,15 +1405,15 @@ QUnit.module('Views', { await testUtils.dom.click(list.$('.o_group_header:first')); // open first group await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_list_record_selector input')); assert.containsOnce(list.el, 'div.o_control_panel .o_cp_action_menus'); - assert.containsNone(list.el, 'div.o_control_panel .o_cp_action_menus .o_dropdown', + assert.containsNone(list.el, 'div.o_control_panel .o_cp_action_menus .dropdown', "should not have dropdown as delete item is not there"); // reload withoud grouping by m2m await list.reload({groupBy: []}); await testUtils.dom.click(list.$('.o_data_row:eq(0) .o_list_record_selector input')); assert.containsOnce(list.el, 'div.o_control_panel .o_cp_action_menus'); - assert.containsOnce(list.el, 'div.o_control_panel .o_cp_action_menus .o_dropdown'); - await testUtils.dom.click(list.$('div.o_control_panel .o_cp_action_menus .o_dropdown button')); + assert.containsOnce(list.el, 'div.o_control_panel .o_cp_action_menus .dropdown'); + await testUtils.dom.click(list.$('div.o_control_panel .o_cp_action_menus .dropdown button')); assert.deepEqual( [...list.el.querySelectorAll('.o_cp_action_menus .o_menu_item')].map((el) => el.innerText), ["Delete"] @@ -2007,7 +2007,7 @@ QUnit.module('Views', { "create button should be invisible"); assert.isVisible($(webClient.el).find('.o_list_button_save'), "save button should be visible"); - await testUtils.dom.click($(webClient.el).find('.o_dropdown_toggler:contains("Group By")')); + await testUtils.dom.click($(webClient.el).find('.dropdown-toggle:contains("Group By")')); await testUtils.dom.click($(webClient.el).find('.o_group_by_menu .o_menu_item:contains("candle")')); assert.isNotVisible($(webClient.el).find('.o_list_button_add'), "create button should be invisible"); @@ -3957,8 +3957,8 @@ QUnit.module('Views', { await click(webClient.el.querySelector('tbody td.o_list_record_selector input')); assert.containsOnce(webClient, '.o_cp_action_menus', 'sidebar should be visible'); - await click(webClient.el.querySelector('.o_cp_action_menus .o_dropdown_toggler')); - const archiveItem = [...webClient.el.querySelectorAll('.o_cp_action_menus .o_dropdown_menu li > a')] + await click(webClient.el.querySelector('.o_cp_action_menus .dropdown-toggle')); + const archiveItem = [...webClient.el.querySelectorAll('.o_cp_action_menus .dropdown-menu li > a')] .filter((elem) => elem.textContent === 'Archive'); await click(archiveItem[0]); assert.strictEqual(document.querySelectorAll('.modal').length, 1, @@ -9497,7 +9497,7 @@ QUnit.module('Views', { // delete a record await testUtils.dom.click(list.$('tbody .o_data_row:first td.o_list_record_selector:first input')); checkSearchRead = true; - await testUtils.dom.click(list.$('.o_dropdown_toggler:contains(Action)')); + await testUtils.dom.click(list.$('.dropdown-toggle:contains(Action)')); await testUtils.dom.click(list.$('a:contains(Delete)')); await testUtils.dom.click($('body .modal button span:contains(Ok)')); assert.strictEqual(list.$('.o_pager_counter').text().trim(), '1-3 / 3', @@ -9547,7 +9547,7 @@ QUnit.module('Views', { // delete a record await testUtils.dom.click(list.$('tbody .o_data_row:first td.o_list_record_selector:first input')); checkSearchRead = true; - await testUtils.dom.click(list.$('.o_dropdown_toggler:contains(Action)')); + await testUtils.dom.click(list.$('.dropdown-toggle:contains(Action)')); await testUtils.dom.click(list.$('a:contains(Delete)')); await testUtils.dom.click($('body .modal button span:contains(Ok)')); @@ -9597,7 +9597,7 @@ QUnit.module('Views', { assert.containsOnce(list, '.o_cp_action_menus', 'sidebar should be available'); // archive all records of current page - await testUtils.dom.click(list.$('.o_cp_action_menus .o_dropdown_toggler:contains(Action)')); + await testUtils.dom.click(list.$('.o_cp_action_menus .dropdown-toggle:contains(Action)')); await testUtils.dom.click(list.$('a:contains(Archive)')); assert.strictEqual($('.modal').length, 1, 'a confirm modal should be displayed'); diff --git a/addons/web/static/tests/legacy/views/pivot_tests.js b/addons/web/static/tests/legacy/views/pivot_tests.js index 0de856711db..8a85ea42361 100644 --- a/addons/web/static/tests/legacy/views/pivot_tests.js +++ b/addons/web/static/tests/legacy/views/pivot_tests.js @@ -6,6 +6,8 @@ var PivotView = require('web.PivotView'); const PivotController = require("web.PivotController"); var testUtils = require('web.test_utils'); var testUtilsDom = require('web.test_utils_dom'); +const { browser } = require('@web/core/browser/browser'); +const { patchWithCleanup } = require('@web/../tests/helpers/utils'); const { registry } = require('@web/core/registry'); const legacyViewRegistry = require('web.view_registry'); @@ -120,6 +122,7 @@ QUnit.module('Views', { }; serverData = { models: this.data }; + patchWithCleanup(browser, { setTimeout: (fn) => fn() }); }, }, function () { QUnit.module('Legacy PivotView'); @@ -622,24 +625,24 @@ QUnit.module('Views', { // click on closed header to open dropdown await testUtils.dom.click(pivot.el.querySelector('tbody .o_pivot_header_cell_closed')); - assert.containsOnce(pivot, ".o_legacy_pivot .o_dropdown_menu"); + assert.containsOnce(pivot, ".o_legacy_pivot .dropdown-menu"); assert.strictEqual( - pivot.el.querySelector(".o_legacy_pivot .o_dropdown_menu").innerText.replace(/\s/g, ""), + pivot.el.querySelector(".o_legacy_pivot .dropdown-menu").innerText.replace(/\s/g, ""), "CompanyTypeCustomerDateOtherProductProductbar" ); // open the Date sub dropdown - await testUtils.dom.click(pivot.el.querySelector(".o_legacy_pivot .o_dropdown_menu .o_dropdown_toggler.o_menu_item")); + await testUtils.dom.triggerEvent(pivot.el.querySelector(".o_legacy_pivot .dropdown-menu .dropdown-toggle.o_menu_item"), "mouseenter"); assert.strictEqual( pivot.el - .querySelector(".o_legacy_pivot .o_dropdown_menu .o_dropdown_menu") + .querySelector(".o_legacy_pivot .dropdown-menu .dropdown-menu") .innerText.replace(/\s/g, ""), "YearQuarterMonthWeekDay" ); await testUtils.dom.click( pivot.el.querySelectorAll( - ".o_legacy_pivot .o_dropdown_menu .o_dropdown_menu .o_dropdown_item" + ".o_legacy_pivot .dropdown-menu .dropdown-menu .dropdown-item" )[2] ); @@ -667,9 +670,9 @@ QUnit.module('Views', { // open dropdown to zoom into first row await testUtils.dom.click(pivot.el.querySelector("tbody .o_pivot_header_cell_closed")); // click on date by day - await testUtils.dom.click(pivot.el.querySelector("tbody .o_dropdown_menu .o_dropdown_toggler")); + await testUtils.dom.triggerEvent(pivot.el.querySelector("tbody .dropdown-menu .dropdown-toggle"), "mouseenter"); await testUtils.dom.click( - pivot.el.querySelector("tbody .o_dropdown_menu .o_dropdown_menu li:nth-child(5)") + pivot.el.querySelector("tbody .dropdown-menu .dropdown-menu span:nth-child(5)") ); // open dropdown to zoom into second row @@ -702,7 +705,7 @@ QUnit.module('Views', { // unfold it await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed')); - await testUtils.dom.click(pivot.el.querySelector(".o_dropdown_menu li:nth-child(5)")); + await testUtils.dom.click(pivot.el.querySelector(".dropdown-menu span:nth-child(5)")); assert.containsN(pivot, 'thead tr', 3); pivot.destroy(); @@ -727,7 +730,7 @@ QUnit.module('Views', { // unfold it await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed:nth(1)')); - await testUtils.dom.click(pivot.el.querySelector(".o_dropdown_menu li:nth-child(1)")); + await testUtils.dom.click(pivot.el.querySelector(".dropdown-menu span:nth-child(1)")); assert.containsN(pivot, 'thead tr', 4); values = ['12', '3', '17', '32']; assert.strictEqual(getCurrentValues(pivot), values.join(',')); @@ -761,22 +764,22 @@ QUnit.module('Views', { // open group by dropdown await cpHelpers.toggleGroupByMenu(pivot.el); - assert.containsN(pivot, '.o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_menu_item', 3, - "should have 2 dropdown items in searchview groupby"); - assert.containsOnce(pivot, '.o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_add_custom_group_menu', + assert.containsN(pivot, '.o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item', 3, + "should have 3 dropdown items in searchview groupby"); + assert.containsOnce(pivot, '.o_control_panel .o_cp_bottom_right .dropdown-menu .o_add_custom_group_menu', "should have custom group generator in searchview groupby"); // click on closed header to open dropdown await testUtils.dom.click(pivot.el.querySelector('tbody tr:last-child .o_pivot_header_cell_closed')); - assert.containsN(pivot, '.o_pivot_field_menu .dropdown-item', 3, + assert.containsN(pivot, '.o_pivot_field_menu .o_menu_item', 3, "should have 3 dropdown items same as searchview groupby"); assert.containsOnce(pivot, '.o_pivot_field_menu .o_add_custom_group_menu', "should have custom group generator same as searchview groupby"); // check custom groupby selection has groupable fields only - await testUtils.dom.click(pivot.$('.o_pivot_field_menu .o_add_custom_group_menu .o_dropdown_toggler')); - assert.containsN(pivot, '.o_pivot_field_menu .o_add_custom_group_menu .o_dropdown_menu option', 6, + await testUtils.dom.triggerEvent(pivot.$('.o_pivot_field_menu .o_add_custom_group_menu .dropdown-toggle'), "mouseenter"); + assert.containsN(pivot, '.o_pivot_field_menu .o_add_custom_group_menu .dropdown-menu option', 6, "should have 6 fields in custom groupby"); - const optionDescriptions = [...pivot.$('.o_pivot_field_menu .o_add_custom_group_menu .o_dropdown_menu option')] + const optionDescriptions = [...pivot.$('.o_pivot_field_menu .o_add_custom_group_menu .dropdown-menu option')] .map(option => option.innerText.trim()); assert.deepEqual( optionDescriptions, @@ -817,14 +820,14 @@ QUnit.module('Views', { // open group by dropdown await cpHelpers.toggleGroupByMenu(pivot.el); - assert.containsN(pivot, '.o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_menu_item', 2, + assert.containsN(pivot, '.o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item', 2, "should have 2 dropdown items in searchview groupby"); // click on closed header to open dropdown await testUtils.dom.click(pivot.el.querySelector('tbody tr:last-child .o_pivot_header_cell_closed')); assert.containsN( pivot.el, - ".o_dropdown_menu .o_menu_item", + ".dropdown-menu .o_menu_item", 2, "should have 2 dropdown items in pivot groupby" ); @@ -832,19 +835,19 @@ QUnit.module('Views', { await cpHelpers.toggleGroupByMenu(pivot.el); await cpHelpers.toggleAddCustomGroup(pivot.el); await cpHelpers.applyGroup(pivot.el); - assert.containsN(pivot, '.o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_menu_item', 3, + assert.containsN(pivot, '.o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item', 3, "should have 3 dropdown items in searchview groupby now"); // click on closed header to open dropdown await testUtils.dom.click(pivot.el.querySelector('tbody tr:last-child .o_pivot_header_cell_closed')); // applying groupby/custom groupby will update pivot groupby dropdown while reverse is not true - assert.containsN(pivot, '.o_pivot_field_menu .dropdown-item', 3, + assert.containsN(pivot, '.o_pivot_field_menu .o_menu_item', 3, "should have 3 dropdown items same as searchview groupby"); // add a custom group in pivot groupby - await testUtils.dom.click(pivot.$('.o_pivot_field_menu .o_add_custom_group_menu .o_dropdown_toggler')); + await testUtils.dom.triggerEvent(pivot.$('.o_pivot_field_menu .o_add_custom_group_menu .dropdown-toggle'), "mouseenter"); await testUtils.fields.editSelect(pivot.$('.o_pivot_field_menu .o_add_custom_group_menu select'), 'date'); - await testUtils.dom.click(pivot.$('.o_pivot_field_menu .o_add_custom_group_menu .o_dropdown_menu button')); + await testUtils.dom.click(pivot.$('.o_pivot_field_menu .o_add_custom_group_menu .dropdown-menu button')); // click on closed header to open groupby selection dropdown await testUtils.dom.click(pivot.el.querySelector('tbody tr:last-child .o_pivot_header_cell_closed')); assert.containsN(pivot, '.o_pivot_field_menu .o_menu_item', 4, @@ -852,7 +855,7 @@ QUnit.module('Views', { // applying custom groupby in pivot groupby dropdown will not update search dropdown await cpHelpers.toggleGroupByMenu(pivot.el); - assert.containsN(pivot, '.o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_menu_item', 3, + assert.containsN(pivot, '.o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item', 3, "should have 3 dropdown items in searchview groupby dropdown"); pivot.destroy(); @@ -882,11 +885,11 @@ QUnit.module('Views', { // open group by dropdown await cpHelpers.toggleGroupByMenu(pivot.el); - assert.containsN(pivot, '.o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_menu_item', 2, + assert.containsN(pivot, '.o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item', 2, "should have 2 dropdown items in searchview groupby"); await cpHelpers.toggleAddCustomGroup(pivot.el); await cpHelpers.applyGroup(pivot.el); - assert.containsN(pivot, '.o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_menu_item', 3, + assert.containsN(pivot, '.o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item', 3, "should have 3 dropdown items in searchview groupby now"); // click on closed header to open dropdown @@ -895,7 +898,7 @@ QUnit.module('Views', { assert.deepEqual([...items].map((el) => el.innerText), ["bar", "product", "Company Type"]); assert.containsN( pivot, - "tbody .o_dropdown_menu .dropdown-divider", 2, + "tbody .dropdown-menu .dropdown-divider", 2, "pivot groupby menu should have two separators" ); assert.hasClass( @@ -940,7 +943,7 @@ QUnit.module('Views', { // click on closed header to open dropdown and apply groupby on date field await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed')); - await testUtils.dom.click(pivot.$('.o_pivot_field_menu .o_add_custom_group_menu .o_dropdown_toggler')); + await testUtils.dom.triggerEvent(pivot.$('.o_pivot_field_menu .o_add_custom_group_menu .dropdown-toggle'), "mouseenter"); await testUtils.fields.editSelect(pivot.$('.o_pivot_field_menu .o_add_custom_group_menu select'), 'date'); checkReadGroup = true; await testUtils.dom.click(pivot.$('.o_pivot_field_menu .o_add_custom_group_menu .btn-primary')); @@ -967,7 +970,7 @@ QUnit.module('Views', { // open group by dropdown await cpHelpers.toggleGroupByMenu(pivot.el); - assert.containsNone(pivot, '.o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_menu_item', + assert.containsNone(pivot, '.o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item', "should not have any dropdown item in searchview groupby"); // click on closed header to open dropdown await testUtils.dom.click(pivot.el.querySelector('tbody tr:last-child .o_pivot_header_cell_closed')); @@ -1013,9 +1016,9 @@ QUnit.module('Views', { // open group by dropdown await cpHelpers.toggleGroupByMenu(pivot.el); - assert.containsOnce(pivot, '.o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_menu_item', + assert.containsOnce(pivot, '.o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item', "should have 1 dropdown item in searchview groupby"); - assert.containsNone(pivot, '.o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_add_custom_group_menu', + assert.containsNone(pivot, '.o_control_panel .o_cp_bottom_right .dropdown-menu .o_add_custom_group_menu', "should not have custom group generator in searchview groupby"); // click on closed header to open dropdown @@ -1051,7 +1054,7 @@ QUnit.module('Views', { // click on closed header to open dropdown and apply groupby on date field await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed')); - await testUtils.dom.click(pivot.$('.o_pivot_field_menu .o_add_custom_group_menu .o_dropdown_toggler')); + await testUtils.dom.triggerEvent(pivot.$('.o_pivot_field_menu .o_add_custom_group_menu .dropdown-toggle'), "mouseenter"); assert.containsOnce(pivot, '.o_pivot_field_menu .o_add_custom_group_menu', "should have custom group generator in groupby dropdown"); @@ -1203,7 +1206,7 @@ QUnit.module('Views', { // tries to open a field selection menu, to make sure it was not // removed from the dom. await testUtils.dom.clickFirst(pivot.$('.o_pivot_header_cell_closed')); - assert.containsOnce(pivot, '.o_dropdown_menu', + assert.containsOnce(pivot, '.o_legacy_pivot .dropdown-menu', "the field selector menu exists"); pivot.destroy(); }); @@ -1488,7 +1491,7 @@ QUnit.module('Views', { // expand header on field customer await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed:nth(1)')); - await testUtils.dom.click(pivot.el.querySelectorAll("thead .o_dropdown_menu .o_dropdown_item")[1]); + await testUtils.dom.click(pivot.el.querySelectorAll("thead .dropdown-menu .dropdown-item")[1]); assert.deepEqual(pivot.getOwnedQueryParams(), { context: { pivot_column_groupby: ['date:day', 'customer'], @@ -1499,7 +1502,7 @@ QUnit.module('Views', { // expand row on field product_id await testUtils.dom.click(pivot.el.querySelector('tbody .o_pivot_header_cell_closed')); - await testUtils.dom.click(pivot.el.querySelectorAll("tbody .o_dropdown_menu .o_dropdown_item")[4]); + await testUtils.dom.click(pivot.el.querySelectorAll("tbody .dropdown-menu .dropdown-item")[4]); assert.deepEqual(pivot.getOwnedQueryParams(), { context: { pivot_column_groupby: ['date:day', 'customer'], @@ -1571,7 +1574,7 @@ QUnit.module('Views', { }, "context should be correct"); await testUtils.dom.click(pivot.el.querySelector('tbody .o_pivot_header_cell_closed')); - await testUtils.dom.click(pivot.$('.o_dropdown_menu li:nth-child(5)')); + await testUtils.dom.click(pivot.$('.dropdown-menu span:nth-child(5)')); assert.deepEqual(pivot.getOwnedQueryParams(), { context: { @@ -1582,7 +1585,7 @@ QUnit.module('Views', { }, "context should be correct"); await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed')); - await testUtils.dom.click(pivot.$('.o_dropdown_menu li:nth-child(2)')); + await testUtils.dom.click(pivot.$('.dropdown-menu span:nth-child(2)')); assert.deepEqual(pivot.getOwnedQueryParams(), { context: { @@ -1696,7 +1699,7 @@ QUnit.module('Views', { // Set a column groupby await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed')); - await testUtils.dom.click(pivot.el.querySelectorAll("thead .o_dropdown_menu .o_dropdown_item")[1]); + await testUtils.dom.click(pivot.el.querySelectorAll("thead .dropdown-menu .dropdown-item")[1]); // Set a domain await pivot.update({domain: [['product_id', '=', 37]], groupBy: [], context: {}}); @@ -1711,7 +1714,7 @@ QUnit.module('Views', { // Set a column groupby await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed')); - await testUtils.dom.click(pivot.el.querySelectorAll("thead .o_dropdown_menu .o_dropdown_item")[4]); + await testUtils.dom.click(pivot.el.querySelectorAll("thead .dropdown-menu .dropdown-item")[4]); // Set a domain await pivot.update({domain: [['product_id', '=', 41]], groupBy: [], context: {}}); @@ -1749,7 +1752,7 @@ QUnit.module('Views', { // expand a col group await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed:nth(1)')); - await testUtils.dom.click(pivot.el.querySelectorAll("thead .o_dropdown_menu .o_dropdown_item")[1]); + await testUtils.dom.click(pivot.el.querySelectorAll("thead .dropdown-menu .dropdown-item")[1]); values = [ "29", "1", "2", "32", @@ -1760,7 +1763,7 @@ QUnit.module('Views', { // expand a row group await testUtils.dom.click(pivot.el.querySelector('tbody tr:last-child .o_pivot_header_cell_closed')); - await testUtils.dom.click(pivot.el.querySelectorAll("tbody .o_dropdown_menu .o_dropdown_item")[3]); + await testUtils.dom.click(pivot.el.querySelectorAll("tbody .dropdown-menu .dropdown-item")[3]); values = [ "29", "1", "2", "32", @@ -1803,7 +1806,7 @@ QUnit.module('Views', { // Set a column groupby await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed')); - await testUtils.dom.click(pivot.el.querySelectorAll("thead .o_dropdown_menu .o_dropdown_item")[1]); + await testUtils.dom.click(pivot.el.querySelectorAll("thead .dropdown-menu .dropdown-item")[1]); // Set a domain for empty results await pivot.update({domain: [['id', '=', false]]}); @@ -1877,21 +1880,21 @@ QUnit.module('Views', { }); await testUtils.dom.click(pivot.el.querySelector('thead .o_pivot_header_cell_closed')); - await testUtils.dom.click(pivot.el.querySelector('thead .o_dropdown_menu .o_dropdown_toggler')); - await testUtils.dom.click(pivot.el.querySelectorAll("thead .o_dropdown_menu .o_dropdown_menu .o_dropdown_item")[3]); + await testUtils.dom.triggerEvent(pivot.el.querySelector('thead .dropdown-menu .dropdown-toggle'), "mouseenter"); + await testUtils.dom.click(pivot.el.querySelectorAll("thead .dropdown-menu .dropdown-menu .dropdown-item")[3]); // close and reopen row groupings after changing value this.data.partner.records.find(r => r.product_id === 37).date = '2016-10-27'; await testUtils.dom.click(pivot.el.querySelector('tbody .o_pivot_header_cell_opened')); await testUtils.dom.click(pivot.el.querySelector('tbody .o_pivot_header_cell_closed')); - await testUtils.dom.click(pivot.el.querySelectorAll("tbody .o_dropdown_menu .o_dropdown_item")[4]); + await testUtils.dom.click(pivot.el.querySelectorAll("tbody .dropdown-menu .dropdown-item")[4]); assert.strictEqual(pivot.el.querySelectorAll('.o_pivot_cell_value')[4].innerText, ''); // xphone December 2016 // invert axis, and reopen column groupings await testUtils.dom.click(pivot.el.querySelector('.o_cp_buttons .o_pivot_flip_button')); await testUtils.dom.click(pivot.el.querySelector('thead .o_pivot_header_cell_opened')); await testUtils.dom.click(pivot.el.querySelector('thead .o_pivot_header_cell_closed')); - await testUtils.dom.click(pivot.el.querySelectorAll("thead .o_dropdown_menu .o_dropdown_item")[4]); + await testUtils.dom.click(pivot.el.querySelectorAll("thead .dropdown-menu .dropdown-item")[4]); assert.strictEqual(pivot.el.querySelectorAll('.o_pivot_cell_value')[3].innerText, ''); // December 2016 xphone pivot.destroy(); @@ -2380,9 +2383,9 @@ QUnit.module('Views', { }); await testUtils.dom.click(pivot.el.querySelector('tbody .o_pivot_header_cell_closed')); // click on date by month - await testUtils.dom.click(pivot.el.querySelector("tbody .o_dropdown_menu .o_dropdown_toggler")); + await testUtils.dom.triggerEvent(pivot.el.querySelector("tbody .dropdown-menu .dropdown-toggle"), "mouseenter"); await testUtils.dom.click( - pivot.el.querySelectorAll("tbody .o_dropdown_menu .o_dropdown_menu .o_dropdown_item")[3] + pivot.el.querySelectorAll("tbody .dropdown-menu .dropdown-menu .dropdown-item")[3] ); assert.strictEqual(pivot.$('.o_pivot_cell_value').text(), '2211', @@ -2426,7 +2429,7 @@ QUnit.module('Views', { await testUtils.dom.click(pivot.el.querySelector('tbody .o_pivot_header_cell_closed')); - await testUtils.dom.click(pivot.el.querySelectorAll("tbody .o_dropdown_item")[4]); + await testUtils.dom.click(pivot.el.querySelectorAll("tbody .dropdown-item")[4]); assert.strictEqual(pivot.$('.o_pivot_cell_value').text(), '211', 'should have loaded the proper data'); @@ -2447,11 +2450,11 @@ QUnit.module('Views', { // Set a column groupby await testUtils.dom.click(pivot.$('thead .o_pivot_header_cell_closed')); - await testUtils.dom.click(pivot.el.querySelectorAll("thead .o_dropdown_item")[1]); + await testUtils.dom.click(pivot.el.querySelectorAll("thead .dropdown-item")[1]); // Set a Row groupby await testUtils.dom.click(pivot.el.querySelector('tbody .o_pivot_header_cell_closed')); - await testUtils.dom.click(pivot.el.querySelectorAll("tbody .o_dropdown_item")[4]); + await testUtils.dom.click(pivot.el.querySelectorAll("tbody .dropdown-item")[4]); // Set a domain await testUtils.pivot.reload(pivot, {domain: [['product_id', '=', 41]]}); @@ -2681,7 +2684,7 @@ QUnit.module('Views', { // with data, with row groupby await testUtils.dom.click(pivot.$('.o_legacy_pivot .o_pivot_header_cell_closed').eq(2)); - await testUtils.dom.click(pivot.el.querySelectorAll("tbody .o_dropdown_menu .o_dropdown_item")[4]); + await testUtils.dom.click(pivot.el.querySelectorAll("tbody .dropdown-menu .dropdown-item")[4]); values = [ "19", "0", "-100%", "0", "13", "100%", "19", "13", "-31.58%", "19", "0", "-100%", "0", "1" , "100%", "19", "1", "-94.74%", @@ -3309,7 +3312,7 @@ QUnit.module('Views', { // Set a Row groupby await testUtils.dom.click(pivot.el.querySelector('tbody tr:nth-child(2) .o_pivot_header_cell_closed')); - await testUtils.dom.click(pivot.el.querySelectorAll("tbody .o_dropdown_item")[4]); + await testUtils.dom.click(pivot.el.querySelectorAll("tbody .dropdown-item")[4]); assert.strictEqual( pivot.$('th').slice(0, 2).text(), @@ -3374,13 +3377,13 @@ QUnit.module('Views', { }, }); await testUtils.dom.click(pivot.el.querySelector("thead th.o_pivot_header_cell_closed")); - await testUtils.dom.click(pivot.el.querySelectorAll("thead .o_dropdown_menu .o_dropdown_item")[5]); + await testUtils.dom.click(pivot.el.querySelectorAll("thead .dropdown-menu .dropdown-item")[5]); // Click on the two dropdown await testUtils.dom.click(pivot.el.querySelectorAll("thead th.o_pivot_header_cell_closed")[0]); await testUtils.dom.click(pivot.el.querySelectorAll("thead th.o_pivot_header_cell_closed")[1]); - assert.containsOnce(pivot, 'thead .o_dropdown_menu', 'Only one dropdown should be displayed at a time'); + assert.containsOnce(pivot, 'thead .dropdown-menu', 'Only one dropdown should be displayed at a time'); pivot.destroy(); }); diff --git a/addons/web/static/tests/legacy/views/state_mapping_tests.js b/addons/web/static/tests/legacy/views/state_mapping_tests.js index 04189dd873d..b6a2e522443 100644 --- a/addons/web/static/tests/legacy/views/state_mapping_tests.js +++ b/addons/web/static/tests/legacy/views/state_mapping_tests.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { legacyExtraNextTick } from "@web/../tests/helpers/utils"; +import { legacyExtraNextTick, patchWithCleanup } from "@web/../tests/helpers/utils"; import { getFacetTexts, removeFacet, @@ -23,6 +23,7 @@ import AbstractView from "web.AbstractView"; import ActionModel from "web.ActionModel"; import { mock } from "web.test_utils"; import legacyViewRegistry from "web.view_registry"; +import { browser } from "@web/core/browser/browser"; const serviceRegistry = registry.category("services"); const viewRegistry = registry.category("views"); @@ -258,6 +259,7 @@ QUnit.module("Views", (hooks) => { async function (assert) { assert.expect(5); + patchWithCleanup(browser, { setTimeout: (fn) => fn() }); const webClient = await createWebClient({ serverData, mockRPC(_, args) { diff --git a/addons/web/static/tests/legacy/views/view_dialogs_tests.js b/addons/web/static/tests/legacy/views/view_dialogs_tests.js index b5c3aa4915c..ce7da1b86a7 100644 --- a/addons/web/static/tests/legacy/views/view_dialogs_tests.js +++ b/addons/web/static/tests/legacy/views/view_dialogs_tests.js @@ -7,6 +7,8 @@ var testUtils = require('web.test_utils'); var Widget = require('web.Widget'); var FormView = require('web.FormView'); +const { browser } = require('@web/core/browser/browser'); +const { patchWithCleanup } = require('@web/../tests/helpers/utils'); const cpHelpers = require('@web/../tests/search/helpers'); var createView = testUtils.createView; @@ -526,6 +528,11 @@ QUnit.module('Views', { }, }); + // save favorite needs this + patchWithCleanup(browser, { + setTimeout: (fn) => fn(), + }); + var parent = await createParent({ data: this.data, archs: { diff --git a/addons/web/static/tests/search/comparison_menu_tests.js b/addons/web/static/tests/search/comparison_menu_tests.js index 8b518524cb0..ccde0a117b8 100644 --- a/addons/web/static/tests/search/comparison_menu_tests.js +++ b/addons/web/static/tests/search/comparison_menu_tests.js @@ -1,6 +1,7 @@ /** @odoo-module **/ -import { patchDate } from "@web/../tests/helpers/utils"; +import { patchDate, patchWithCleanup } from "@web/../tests/helpers/utils"; +import { browser } from "@web/core/browser/browser"; import { ControlPanel } from "@web/search/control_panel/control_panel"; import { getFacetTexts, @@ -35,6 +36,10 @@ QUnit.module("Search", (hooks) => { }, }; setupControlPanelServiceRegistry(); + patchWithCleanup(browser, { + setTimeout: (fn) => fn(), + clearTimeout: () => {}, + }); }); QUnit.module("Comparison"); @@ -49,8 +54,8 @@ QUnit.module("Search", (hooks) => { searchMenuTypes: ["filter", "comparison"], searchViewId: false, }); - assert.containsOnce(controlPanel, ".o_dropdown.o_filter_menu"); - assert.containsNone(controlPanel, ".o_dropdown.o_comparison_menu"); + assert.containsOnce(controlPanel, ".dropdown.o_filter_menu"); + assert.containsNone(controlPanel, ".dropdown.o_comparison_menu"); await toggleFilterMenu(controlPanel); await toggleMenuItem(controlPanel, "Birthday"); await toggleMenuItemOption(controlPanel, "Birthday", "January"); @@ -63,7 +68,9 @@ QUnit.module("Search", (hooks) => { "COMPARISON" ); await toggleComparisonMenu(controlPanel); - const comparisonOptions = [...controlPanel.el.querySelectorAll(".o_comparison_menu li")]; + const comparisonOptions = [ + ...controlPanel.el.querySelectorAll(".o_comparison_menu .dropdown-item"), + ]; assert.strictEqual(comparisonOptions.length, 2); assert.deepEqual( comparisonOptions.map((e) => e.innerText.trim()), diff --git a/addons/web/static/tests/search/custom_favorite_item_tests.js b/addons/web/static/tests/search/custom_favorite_item_tests.js index a63dc326a2b..9dfa9bfbea1 100644 --- a/addons/web/static/tests/search/custom_favorite_item_tests.js +++ b/addons/web/static/tests/search/custom_favorite_item_tests.js @@ -1,6 +1,7 @@ /** @odoo-module **/ -import { triggerEvent } from "@web/../tests/helpers/utils"; +import { patchWithCleanup, triggerEvent } from "@web/../tests/helpers/utils"; +import { browser } from "@web/core/browser/browser"; import { dialogService } from "@web/core/dialog/dialog_service"; import { registry } from "@web/core/registry"; import { ControlPanel } from "@web/search/control_panel/control_panel"; @@ -62,6 +63,10 @@ QUnit.module("Search", (hooks) => { setupControlPanelFavoriteMenuRegistry(); setupControlPanelServiceRegistry(); serviceRegistry.add("dialog", dialogService); + patchWithCleanup(browser, { + setTimeout: (fn) => fn(), + clearTimeout: () => {}, + }); }); QUnit.module("CustomFavoriteItem"); diff --git a/addons/web/static/tests/search/custom_filter_item_tests.js b/addons/web/static/tests/search/custom_filter_item_tests.js index 918f5115eff..5ba239852a3 100644 --- a/addons/web/static/tests/search/custom_filter_item_tests.js +++ b/addons/web/static/tests/search/custom_filter_item_tests.js @@ -4,6 +4,7 @@ import { registerCleanup } from "@web/../tests/helpers/cleanup"; import { click, patchDate, patchWithCleanup } from "@web/../tests/helpers/utils"; import { localization } from "@web/core/l10n/localization"; import { ControlPanel } from "@web/search/control_panel/control_panel"; +import { browser } from "@web/core/browser/browser"; import { addCondition, applyFilter, @@ -84,6 +85,10 @@ QUnit.module("Search", (hooks) => { }, }; setupControlPanelServiceRegistry(); + patchWithCleanup(browser, { + setTimeout: (fn) => fn(), + clearTimeout: () => {}, + }); }); QUnit.module("CustomFilterItem"); @@ -305,13 +310,13 @@ QUnit.module("Search", (hooks) => { assert.deepEqual(getDomain(controlPanel), []); assert.containsNone(controlPanel, ".o_menu_item"); - assert.containsOnce(controlPanel, ".o_add_custom_filter_menu button.o_dropdown_toggler"); + assert.containsOnce(controlPanel, ".o_add_custom_filter_menu button.dropdown-toggle"); // the 'Add Custom Filter' menu should be closed; - assert.containsNone(controlPanel, ".o_add_custom_filter_menu ul.o_dropdown_menu"); + assert.containsNone(controlPanel, ".o_add_custom_filter_menu .dropdown-menu"); await toggleAddCustomFilter(controlPanel); // the 'Add Custom Filter' menu should be open; - assert.containsOnce(controlPanel, ".o_add_custom_filter_menu ul.o_dropdown_menu"); + assert.containsOnce(controlPanel, ".o_add_custom_filter_menu .dropdown-menu"); await applyFilter(controlPanel); @@ -319,9 +324,9 @@ QUnit.module("Search", (hooks) => { assert.deepEqual(getDomain(controlPanel), [["boolean_field", "=", true]]); assert.containsOnce(controlPanel, ".o_menu_item"); - assert.containsOnce(controlPanel, ".o_add_custom_filter_menu button.o_dropdown_toggler"); - // the 'Add Custom Filter' menu should be closed; - assert.containsNone(controlPanel, ".o_add_custom_filter_menu ul.o_dropdown_menu"); + assert.containsOnce(controlPanel, ".o_add_custom_filter_menu button.dropdown-toggle"); + // the 'Add Custom Filter' menu should still be opened; + assert.containsOnce(controlPanel, ".o_add_custom_filter_menu .dropdown-menu"); }); QUnit.test("selection field: default and updated value", async function (assert) { diff --git a/addons/web/static/tests/search/custom_group_by_item_tests.js b/addons/web/static/tests/search/custom_group_by_item_tests.js index 896972959f9..e6da6ff60a9 100644 --- a/addons/web/static/tests/search/custom_group_by_item_tests.js +++ b/addons/web/static/tests/search/custom_group_by_item_tests.js @@ -1,6 +1,8 @@ /** @odoo-module **/ +import { browser } from "@web/core/browser/browser"; import { ControlPanel } from "@web/search/control_panel/control_panel"; +import { patchWithCleanup } from "../helpers/utils"; import { applyGroup, getFacetTexts, @@ -34,6 +36,10 @@ QUnit.module("Search", (hooks) => { }, }; setupControlPanelServiceRegistry(); + patchWithCleanup(browser, { + setTimeout: (fn) => fn(), + clearTimeout: () => {}, + }); }); QUnit.module("CustomGroupByItem"); @@ -54,12 +60,12 @@ QUnit.module("Search", (hooks) => { const customGroupByItem = controlPanel.el.querySelector(".o_add_custom_group_menu"); assert.strictEqual(customGroupByItem.innerText.trim(), "Add Custom Group"); - assert.containsOnce(customGroupByItem, "button.o_dropdown_toggler"); - assert.containsNone(customGroupByItem, "ul.o_dropdown_menu"); + assert.containsOnce(customGroupByItem, "button.dropdown-toggle"); + assert.containsNone(customGroupByItem, ".dropdown-menu"); await toggleAddCustomGroup(controlPanel); - assert.containsOnce(customGroupByItem, "ul.o_dropdown_menu"); + assert.containsOnce(customGroupByItem, ".dropdown-menu"); assert.deepEqual( [...controlPanel.el.querySelectorAll(".o_add_custom_group_menu select option")].map( @@ -92,7 +98,7 @@ QUnit.module("Search", (hooks) => { assert.deepEqual( [ ...controlPanel.el.querySelectorAll( - ".o_add_custom_group_menu .o_dropdown_menu select option" + ".o_add_custom_group_menu .dropdown-menu select option" ), ].map((el) => el.innerText), ["Foo"] @@ -156,16 +162,16 @@ QUnit.module("Search", (hooks) => { await toggleAddCustomGroup(controlPanel); // Single select node with a single option - assert.containsOnce(controlPanel, ".o_add_custom_group_menu .o_dropdown_menu select"); + assert.containsOnce(controlPanel, ".o_add_custom_group_menu .dropdown-menu select"); assert.strictEqual( controlPanel.el - .querySelector(".o_add_custom_group_menu .o_dropdown_menu select option") + .querySelector(".o_add_custom_group_menu .dropdown-menu select option") .innerText.trim(), "Super Date" ); // Button apply - assert.containsOnce(controlPanel, ".o_add_custom_group_menu .o_dropdown_menu .btn"); + assert.containsOnce(controlPanel, ".o_add_custom_group_menu .dropdown-menu .btn"); }); QUnit.test( @@ -192,8 +198,8 @@ QUnit.module("Search", (hooks) => { await applyGroup(controlPanel); assert.containsOnce(controlPanel, ".o_group_by_menu .o_menu_item"); - assert.containsOnce(controlPanel, ".o_add_custom_group_menu .o_dropdown_toggler"); - assert.containsNone(controlPanel, ".o_add_custom_group_menu .o_dropdown_menu"); + assert.containsOnce(controlPanel, ".o_add_custom_group_menu .dropdown-toggle"); + assert.containsOnce(controlPanel, ".o_add_custom_group_menu .dropdown-menu"); assert.deepEqual(getFacetTexts(controlPanel), ["Candlelight"]); } ); diff --git a/addons/web/static/tests/search/favorite_menu_tests.js b/addons/web/static/tests/search/favorite_menu_tests.js index 0851aded1f7..a6d86a61432 100644 --- a/addons/web/static/tests/search/favorite_menu_tests.js +++ b/addons/web/static/tests/search/favorite_menu_tests.js @@ -84,10 +84,10 @@ QUnit.module("Search", (hooks) => { await toggleFavoriteMenu(controlPanel); assert.containsOnce( controlPanel, - "div.o_favorite_menu > .o_dropdown_menu", + "div.o_favorite_menu > .dropdown-menu", "the menu should be opened" ); - assert.containsNone(controlPanel, ".o_dropdown_menu *", "the menu should be empty"); + assert.containsNone(controlPanel, ".dropdown-menu *", "the menu should be empty"); } ); @@ -117,11 +117,11 @@ QUnit.module("Search", (hooks) => { await toggleFavoriteMenu(controlPanel); assert.containsOnce( controlPanel, - "div.o_favorite_menu > .o_dropdown_menu", + "div.o_favorite_menu > .dropdown-menu", "the menu should be opened" ); - assert.containsNone(controlPanel, ".o_dropdown_menu .dropdown-divider"); - assert.containsOnce(controlPanel, ".o_dropdown_menu .o_add_favorite"); + assert.containsNone(controlPanel, ".dropdown-menu .dropdown-divider"); + assert.containsOnce(controlPanel, ".dropdown-menu .o_add_favorite"); }); QUnit.test( @@ -182,7 +182,7 @@ QUnit.module("Search", (hooks) => { assert.deepEqual(getFacetTexts(webClient), ["My favorite"]); assert.hasClass( - webClient.el.querySelector(".o_favorite_menu li.o_menu_item"), + webClient.el.querySelector(".o_favorite_menu .o_menu_item"), "selected" ); diff --git a/addons/web/static/tests/search/filter_menu_tests.js b/addons/web/static/tests/search/filter_menu_tests.js index d4738266870..b3948c278d3 100644 --- a/addons/web/static/tests/search/filter_menu_tests.js +++ b/addons/web/static/tests/search/filter_menu_tests.js @@ -1,6 +1,7 @@ /** @odoo-module **/ -import { patchDate } from "@web/../tests/helpers/utils"; +import { patchDate, patchWithCleanup } from "@web/../tests/helpers/utils"; +import { browser } from "@web/core/browser/browser"; import { ControlPanel } from "@web/search/control_panel/control_panel"; import { getFacetTexts, @@ -45,6 +46,10 @@ QUnit.module("Search", (hooks) => { }, }; setupControlPanelServiceRegistry(); + patchWithCleanup(browser, { + setTimeout: (fn) => fn(), + clearTimeout: () => {}, + }); }); QUnit.module("FilterMenu"); @@ -147,7 +152,7 @@ QUnit.module("Search", (hooks) => { await toggleFilterMenu(controlPanel); await toggleMenuItem(controlPanel, "Date"); - const optionEls = controlPanel.el.querySelectorAll(".o_dropdown .o_item_option"); + const optionEls = controlPanel.el.querySelectorAll(".dropdown .o_item_option"); // default filter should be activated with the global default period 'this_month' assert.deepEqual(getDomain(controlPanel), [ diff --git a/addons/web/static/tests/search/group_by_menu_tests.js b/addons/web/static/tests/search/group_by_menu_tests.js index 728a5dd87ed..d9b9c1da368 100644 --- a/addons/web/static/tests/search/group_by_menu_tests.js +++ b/addons/web/static/tests/search/group_by_menu_tests.js @@ -1,6 +1,8 @@ /** @odoo-module **/ +import { browser } from "@web/core/browser/browser"; import { ControlPanel } from "@web/search/control_panel/control_panel"; +import { patchWithCleanup } from "../helpers/utils"; import { getFacetTexts, isItemSelected, @@ -34,6 +36,10 @@ QUnit.module("Search", (hooks) => { }, }; setupControlPanelServiceRegistry(); + patchWithCleanup(browser, { + setTimeout: (fn) => fn(), + clearTimeout: () => {}, + }); }); QUnit.module("GroupByMenu"); diff --git a/addons/web/static/tests/search/helpers.js b/addons/web/static/tests/search/helpers.js index 6d9ba334435..fa5a39825a4 100644 --- a/addons/web/static/tests/search/helpers.js +++ b/addons/web/static/tests/search/helpers.js @@ -10,7 +10,7 @@ import { viewService } from "@web/views/view_service"; import { actionService } from "@web/webclient/actions/action_service"; import { registerCleanup } from "../helpers/cleanup"; import { makeTestEnv } from "../helpers/mock_env"; -import { click, getFixture, triggerEvent } from "../helpers/utils"; +import { click, getFixture, mouseEnter, triggerEvent } from "../helpers/utils"; const serviceRegistry = registry.category("services"); const favoriteMenuRegistry = registry.category("favoriteMenu"); @@ -72,18 +72,26 @@ const findItem = (target, selector, finder = 0) => { /** Menu (generic) */ export const toggleMenu = async (el, menuFinder) => { - const menu = findItem(el, `.o_dropdown button.o_dropdown_toggler`, menuFinder); + const menu = findItem(el, `.dropdown button.dropdown-toggle`, menuFinder); await click(menu); }; export const toggleMenuItem = async (el, itemFinder) => { const item = findItem(el, `.o_menu_item`, itemFinder); - await click(item); + if (item.classList.contains("dropdown-toggle")) { + await mouseEnter(item); + } else { + await click(item); + } }; export const toggleMenuItemOption = async (el, itemFinder, optionFinder) => { const item = findItem(el, `.o_menu_item`, itemFinder); const option = findItem(item.parentNode, ".o_item_option", optionFinder); - await click(option); + if (option.classList.contains("dropdown-toggle")) { + await mouseEnter(option); + } else { + await click(option); + } }; export const isItemSelected = (el, itemFinder) => { const item = findItem(el, `.o_menu_item`, itemFinder); @@ -96,17 +104,17 @@ export const isOptionSelected = (el, itemFinder, optionFinder) => { }; export const getMenuItemTexts = (target) => { const el = getNode(target); - return [...el.querySelectorAll(`.o_dropdown ul .o_menu_item`)].map((e) => e.innerText.trim()); + return [...el.querySelectorAll(`.dropdown ul .o_menu_item`)].map((e) => e.innerText.trim()); }; /** Filter menu */ export const toggleFilterMenu = async (el) => { - await click(findItem(el, `.o_filter_menu button.o_dropdown_toggler`)); + await click(findItem(el, `.o_filter_menu button.dropdown-toggle`)); }; export const toggleAddCustomFilter = async (el) => { - await click(findItem(el, `.o_add_custom_filter_menu .o_dropdown_toggler`)); + await mouseEnter(findItem(el, `.o_add_custom_filter_menu .dropdown-toggle`)); }; export const editConditionField = async (el, index, fieldName) => { @@ -135,11 +143,11 @@ export const editConditionValue = async (el, index, value, valueIndex = 0) => { }; export const applyFilter = async (el) => { - await click(findItem(el, `.o_add_custom_filter_menu .o_dropdown_menu button.o_apply_filter`)); + await click(findItem(el, `.o_add_custom_filter_menu .dropdown-menu button.o_apply_filter`)); }; export const addCondition = async (el) => { - await click(findItem(el, `.o_add_custom_filter_menu .o_dropdown_menu button.o_add_condition`)); + await click(findItem(el, `.o_add_custom_filter_menu .dropdown-menu button.o_add_condition`)); }; export async function removeCondition(el, index) { @@ -150,27 +158,27 @@ export async function removeCondition(el, index) { /** Group by menu */ export const toggleGroupByMenu = async (el) => { - await click(findItem(el, `.o_group_by_menu .o_dropdown_toggler`)); + await click(findItem(el, `.o_group_by_menu .dropdown-toggle`)); }; export const toggleAddCustomGroup = async (el) => { - await click(findItem(el, `.o_add_custom_group_menu .o_dropdown_toggler`)); + await mouseEnter(findItem(el, `.o_add_custom_group_menu .dropdown-toggle`)); }; export const selectGroup = async (el, fieldName) => { - const select = findItem(el, `.o_add_custom_group_menu .o_dropdown_menu select`); + const select = findItem(el, `.o_add_custom_group_menu .dropdown-menu select`); select.value = fieldName; await triggerEvent(select, null, "change"); }; export const applyGroup = async (el) => { - await click(findItem(el, `.o_add_custom_group_menu .o_dropdown_menu .btn`)); + await click(findItem(el, `.o_add_custom_group_menu .dropdown-menu .btn`)); }; /** Favorite menu */ export const toggleFavoriteMenu = async (el) => { - await click(findItem(el, `.o_favorite_menu .o_dropdown_toggler`)); + await click(findItem(el, `.o_favorite_menu .dropdown-toggle`)); }; export const deleteFavorite = async (el, favoriteFinder) => { @@ -179,26 +187,26 @@ export const deleteFavorite = async (el, favoriteFinder) => { }; export const toggleSaveFavorite = async (el) => { - await click(findItem(el, `.o_favorite_menu .o_add_favorite .o_dropdown_toggler`)); + await mouseEnter(findItem(el, `.o_favorite_menu .o_add_favorite .dropdown-toggle`)); }; export const editFavoriteName = async (el, name) => { const input = findItem( el, - `.o_favorite_menu .o_add_favorite .o_dropdown_menu input[type="text"]` + `.o_favorite_menu .o_add_favorite .dropdown-menu input[type="text"]` ); input.value = name; await triggerEvent(input, null, "input"); }; export const saveFavorite = async (el) => { - await click(findItem(el, `.o_favorite_menu .o_add_favorite .o_dropdown_menu button`)); + await click(findItem(el, `.o_favorite_menu .o_add_favorite .dropdown-menu button`)); }; /** Comparison menu */ export const toggleComparisonMenu = async (el) => { - await click(findItem(el, `.o_comparison_menu button.o_dropdown_toggler`)); + await click(findItem(el, `.o_comparison_menu button.dropdown-toggle`)); }; /** Search bar */ diff --git a/addons/web/static/tests/views/graph_view_tests.js b/addons/web/static/tests/views/graph_view_tests.js index 1670d9c6c74..3d5f7584ace 100644 --- a/addons/web/static/tests/views/graph_view_tests.js +++ b/addons/web/static/tests/views/graph_view_tests.js @@ -22,6 +22,8 @@ import { dialogService } from "@web/core/dialog/dialog_service"; import { registry } from "@web/core/registry"; import { BORDER_WHITE, DEFAULT_BG } from "@web/views/graph/colors"; import { GraphArchParser } from "@web/views/graph/graph_arch_parser"; +import { browser } from "@web/core/browser/browser"; +import { patchWithCleanup } from "../helpers/utils"; const serviceRegistry = registry.category("services"); @@ -277,6 +279,7 @@ QUnit.module("Views", (hooks) => { setupControlPanelServiceRegistry(); setupControlPanelFavoriteMenuRegistry(); serviceRegistry.add("dialog", dialogService); + patchWithCleanup(browser, { setTimeout: (fn) => fn() }); }); QUnit.module("GraphView"); @@ -2478,7 +2481,7 @@ QUnit.module("Views", (hooks) => { }); await toggleMenu(graph, "Measures"); assert.deepEqual( - [...graph.el.querySelectorAll(".o_cp_bottom_left li.o_menu_item")].map( + [...graph.el.querySelectorAll(".o_cp_bottom_left .o_menu_item")].map( (el) => el.innerText ), ["Foo", "Revenue", "Count"] @@ -2523,7 +2526,7 @@ QUnit.module("Views", (hooks) => { }); await toggleMenu(graph, "Measures"); assert.deepEqual( - [...graph.el.querySelectorAll(".o_cp_bottom_left li.o_menu_item")].map( + [...graph.el.querySelectorAll(".o_cp_bottom_left .o_menu_item")].map( (el) => el.innerText ), ["Bouh", "Foo", "Revenue", "Count"] @@ -2900,13 +2903,13 @@ QUnit.module("Views", (hooks) => { await toggleMenu(graph, "Measures"); assert.containsN( graph, - "li.o_menu_item", + ".o_menu_item", 3, "there should be three menu item in the measures dropdown (count, revenue and foo)" ); - assert.containsOnce(graph, 'li.o_menu_item:contains("Revenue")'); - assert.containsOnce(graph, 'li.o_menu_item:contains("Foo")'); - assert.containsOnce(graph, 'li.o_menu_item:contains("Count")'); + assert.containsOnce(graph, '.o_menu_item:contains("Revenue")'); + assert.containsOnce(graph, '.o_menu_item:contains("Foo")'); + assert.containsOnce(graph, '.o_menu_item:contains("Count")'); }); QUnit.test("graph view with invisible attribute on field", async function (assert) { @@ -2924,11 +2927,11 @@ QUnit.module("Views", (hooks) => { await toggleMenu(graph, "Measures"); assert.containsN( graph, - "li.o_menu_item", + ".o_menu_item", 2, "there should be only two menu item in the measures dropdown (count and foo)" ); - assert.containsNone(graph, 'li.o_menu_item:contains("Revenue")'); + assert.containsNone(graph, '.o_menu_item:contains("Revenue")'); }); QUnit.test("graph view sort by measure", async function (assert) { diff --git a/addons/web/static/tests/views/pivot_view_tests.js b/addons/web/static/tests/views/pivot_view_tests.js index d511b682fa8..f08ce150816 100644 --- a/addons/web/static/tests/views/pivot_view_tests.js +++ b/addons/web/static/tests/views/pivot_view_tests.js @@ -14,6 +14,7 @@ import { patchWithCleanup, triggerEvent, triggerEvents, + mouseEnter, } from "../helpers/utils"; import { applyGroup, @@ -35,6 +36,7 @@ import { } from "../search/helpers"; import { createWebClient, doAction } from "../webclient/helpers"; import { makeView } from "./helpers"; +import { browser } from "@web/core/browser/browser"; const serviceRegistry = registry.category("services"); @@ -189,6 +191,7 @@ QUnit.module("Views", (hooks) => { serviceRegistry.add("dialog", dialogService); serviceRegistry.add("localization", makeFakeLocalizationService()); serviceRegistry.add("user", makeFakeUserService()); + patchWithCleanup(browser, { setTimeout: (fn) => fn() }); }); QUnit.module("PivotView"); @@ -257,9 +260,9 @@ QUnit.module("Views", (hooks) => { `, }); - await click(pivot.el.querySelector(".o_cp_bottom_left button.o_dropdown_toggler")); + await click(pivot.el.querySelector(".o_cp_bottom_left button.dropdown-toggle")); assert.strictEqual( - pivot.el.querySelector(".o_cp_bottom_left .o_dropdown_menu .o_dropdown_item").innerText, + pivot.el.querySelector(".o_cp_bottom_left .dropdown-menu .dropdown-item").innerText, "BAR" ); assert.strictEqual( @@ -316,12 +319,12 @@ QUnit.module("Views", (hooks) => { // there should be only one displayed measure as the other one is invisible assert.containsOnce(pivot, ".o_pivot_measure_row"); - await click(pivot.el.querySelector(".o_cp_bottom_left button.o_dropdown_toggler")); + await click(pivot.el.querySelector(".o_cp_bottom_left button.dropdown-toggle")); // there should be only one measure besides count, as the other one is invisible - assert.containsN(pivot, ".o_cp_bottom_left .o_dropdown_menu .dropdown-item", 2); + assert.containsN(pivot, ".o_cp_bottom_left .dropdown-menu .dropdown-item", 2); // the invisible field souldn't be in the groupable fields neither await click(pivot.el.querySelector(".o_pivot_header_cell_closed")); - assert.containsNone(pivot, '.o_dropdown_menu a[data-field="foo2"]'); + assert.containsNone(pivot, '.dropdown-menu a[data-field="foo2"]'); }); QUnit.test('pivot view without "string" attribute', async function (assert) { @@ -382,10 +385,10 @@ QUnit.module("Views", (hooks) => { `, }); - await click(pivot.el.querySelector(".o_cp_bottom_left button.o_dropdown_toggler")); + await click(pivot.el.querySelector(".o_cp_bottom_left button.dropdown-toggle")); assert.containsOnce( pivot, - ".o_cp_bottom_left .o_dropdown_menu .dropdown-item:contains(Computed and not stored)" + ".o_cp_bottom_left .dropdown-menu .dropdown-item:contains(Computed and not stored)" ); assert.strictEqual( pivot.el.querySelector(".o_pivot_measure_row").innerText, @@ -772,11 +775,9 @@ QUnit.module("Views", (hooks) => { }, }); - await click(pivot.el.querySelector(".o_cp_bottom_left .o_dropdown_toggler")); - assert.containsOnce(pivot.el, ".o_cp_bottom_left .o_dropdown_menu .o_dropdown_item"); - const measure = pivot.el.querySelector( - ".o_cp_bottom_left .o_dropdown_menu .o_dropdown_item" - ); + await click(pivot.el.querySelector(".o_cp_bottom_left .dropdown-toggle")); + assert.containsOnce(pivot.el, ".o_cp_bottom_left .dropdown-menu .dropdown-item"); + const measure = pivot.el.querySelector(".o_cp_bottom_left .dropdown-menu .dropdown-item"); assert.strictEqual(measure.innerText, "Count"); assert.hasClass(measure, "selected", "The count measure should be selected"); }); @@ -873,25 +874,23 @@ QUnit.module("Views", (hooks) => { // click on closed header to open dropdown await click(pivot.el, "tbody .o_pivot_header_cell_closed"); - assert.containsOnce(pivot, ".o_pivot .o_dropdown_menu"); + assert.containsOnce(pivot, ".o_pivot .dropdown-menu"); assert.strictEqual( - pivot.el.querySelector(".o_pivot .o_dropdown_menu").innerText.replace(/\s/g, ""), + pivot.el.querySelector(".o_pivot .dropdown-menu").innerText.replace(/\s/g, ""), "CompanyTypeCustomerDateOtherProductProductbarAddCustomGroup" ); // open the Date sub dropdown - await click(pivot.el, ".o_pivot .o_dropdown_menu .o_dropdown_toggler.o_menu_item"); + await mouseEnter(pivot.el, ".o_pivot .dropdown-menu .dropdown-toggle.o_menu_item"); assert.strictEqual( pivot.el - .querySelector(".o_pivot .o_dropdown_menu .o_dropdown_menu") + .querySelector(".o_pivot .dropdown-menu .dropdown-menu") .innerText.replace(/\s/g, ""), "YearQuarterMonthWeekDay" ); await click( - pivot.el.querySelectorAll( - ".o_pivot .o_dropdown_menu .o_dropdown_menu .o_dropdown_item" - )[2] + pivot.el.querySelectorAll(".o_pivot .dropdown-menu .dropdown-menu .dropdown-item")[2] ); assert.containsN(pivot, "tbody tr", 4, "should have 4 rows: one for header, 3 for data"); @@ -919,9 +918,9 @@ QUnit.module("Views", (hooks) => { // open dropdown to zoom into first row await click(pivot.el.querySelector("tbody .o_pivot_header_cell_closed")); // click on date by day - await click(pivot.el.querySelector("tbody .o_dropdown_menu .o_dropdown_toggler")); + await mouseEnter(pivot.el.querySelector("tbody .dropdown-menu .dropdown-toggle")); await click( - pivot.el.querySelector("tbody .o_dropdown_menu .o_dropdown_menu li:nth-child(5)") + pivot.el.querySelector("tbody .dropdown-menu .dropdown-menu span:nth-child(5)") ); // open dropdown to zoom into second row @@ -957,7 +956,7 @@ QUnit.module("Views", (hooks) => { // unfold it await click(pivot.el.querySelector("thead .o_pivot_header_cell_closed")); - await click(pivot.el.querySelector(".o_dropdown_menu li:nth-child(5)")); + await click(pivot.el.querySelector(".dropdown-menu span:nth-child(5)")); assert.containsN(pivot, "thead tr", 3); }); @@ -981,7 +980,7 @@ QUnit.module("Views", (hooks) => { // unfold it await click(pivot.el.querySelector("thead .o_pivot_header_cell_closed:last-child")); - await click(pivot.el.querySelector(".o_dropdown_menu li:nth-child(1)")); + await click(pivot.el.querySelector(".dropdown-menu span:nth-child(1)")); assert.containsN(pivot, "thead tr", 4); values = ["12", "3", "17", "32"]; assert.strictEqual(getCurrentValues(pivot), values.join(",")); @@ -1015,13 +1014,13 @@ QUnit.module("Views", (hooks) => { await toggleGroupByMenu(pivot); assert.containsN( pivot, - ".o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_menu_item", + ".o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item", 3, "should have 3 dropdown items in searchview groupby" ); assert.containsOnce( pivot, - ".o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_add_custom_group_menu", + ".o_control_panel .o_cp_bottom_right .dropdown-menu .o_add_custom_group_menu", "should have custom group generator in searchview groupby" ); @@ -1029,26 +1028,26 @@ QUnit.module("Views", (hooks) => { await click(pivot.el, "tbody tr:last-child .o_pivot_header_cell_closed"); assert.containsN( pivot, - ".o_dropdown_menu > .dropdown-item", + ".dropdown-menu > .dropdown-item", 3, "should have 3 dropdown items same as searchview groupby" ); assert.containsOnce( pivot, - ".o_dropdown_menu .o_add_custom_group_menu", + ".dropdown-menu .o_add_custom_group_menu", "should have custom group generator same as searchview groupby" ); // check custom groupby selection has groupable fields only - await click(pivot.el, ".o_dropdown_menu .o_add_custom_group_menu .o_dropdown_toggler"); + await mouseEnter(pivot.el, ".dropdown-menu .o_add_custom_group_menu .dropdown-toggle"); assert.containsN( pivot, - ".o_dropdown_menu .o_add_custom_group_menu .o_dropdown_menu option", + ".dropdown-menu .o_add_custom_group_menu .dropdown-menu option", 6, "should have 6 fields in custom groupby" ); const optionDescriptions = [ ...pivot.el.querySelectorAll( - ".o_dropdown_menu .o_add_custom_group_menu .o_dropdown_menu option" + ".dropdown-menu .o_add_custom_group_menu .dropdown-menu option" ), ].map((option) => option.innerText.trim()); assert.deepEqual( @@ -1080,7 +1079,7 @@ QUnit.module("Views", (hooks) => { await toggleGroupByMenu(pivot); assert.containsN( pivot, - ".o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_menu_item", + ".o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item", 2, "should have 2 dropdown items in searchview groupby" ); @@ -1089,7 +1088,7 @@ QUnit.module("Views", (hooks) => { await click(pivot.el, "tbody tr:last-child .o_pivot_header_cell_closed"); assert.containsN( pivot, - ".o_dropdown_menu .o_menu_item", + ".dropdown-menu .o_menu_item", 2, "should have 2 dropdown items in pivot groupby" ); @@ -1100,28 +1099,28 @@ QUnit.module("Views", (hooks) => { await applyGroup(pivot); assert.containsN( pivot, - ".o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_menu_item", + ".o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item", 3, "should have 3 dropdown items in searchview groupby now" ); await click(pivot.el, "tbody tr:last-child .o_pivot_header_cell_closed"); assert.containsN( pivot, - ".o_dropdown_menu .o_menu_item", + ".dropdown-menu .o_menu_item", 2, "should still have 2 dropdown items in pivot groupby" ); // add a custom group in pivot groupby - await click(pivot.el, ".o_dropdown_menu .o_add_custom_group_menu .o_dropdown_toggler"); - pivot.el.querySelector(".o_dropdown_menu .o_add_custom_group_menu select").value = "date"; - await triggerEvent(pivot.el, ".o_dropdown_menu .o_add_custom_group_menu select", "change"); - await click(pivot.el, ".o_dropdown_menu .o_add_custom_group_menu .o_dropdown_menu .btn"); + await mouseEnter(pivot.el, ".dropdown-menu .o_add_custom_group_menu .dropdown-toggle"); + pivot.el.querySelector(".dropdown-menu .o_add_custom_group_menu select").value = "date"; + await triggerEvent(pivot.el, ".dropdown-menu .o_add_custom_group_menu select", "change"); + await click(pivot.el, ".dropdown-menu .o_add_custom_group_menu .dropdown-menu .btn"); // click on closed header to open groupby selection dropdown await click(pivot.el, "tbody tr:last-child .o_pivot_header_cell_closed"); assert.containsN( pivot, - ".o_dropdown_menu .o_menu_item", + ".dropdown-menu .o_menu_item", 3, "should have 3 dropdown items in pivot groupby dropdown" ); @@ -1130,7 +1129,7 @@ QUnit.module("Views", (hooks) => { await toggleGroupByMenu(pivot); assert.containsN( pivot, - ".o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_menu_item", + ".o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item", 3, "should still have 3 dropdown items in searchview groupby dropdown" ); @@ -1159,7 +1158,7 @@ QUnit.module("Views", (hooks) => { await toggleGroupByMenu(pivot); assert.containsN( pivot, - ".o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_menu_item", + ".o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item", 2, "should have 2 dropdown items in searchview groupby" ); @@ -1167,7 +1166,7 @@ QUnit.module("Views", (hooks) => { await applyGroup(pivot); assert.containsN( pivot, - ".o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_menu_item", + ".o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item", 3, "should have 3 dropdown items in searchview groupby now" ); @@ -1181,7 +1180,7 @@ QUnit.module("Views", (hooks) => { ); assert.containsOnce( pivot, - "tbody .o_dropdown_menu .dropdown-divider", + "tbody .dropdown-menu .dropdown-divider", "pivot groupby menu should only have one separator" ); assert.hasClass( @@ -1191,17 +1190,14 @@ QUnit.module("Views", (hooks) => { ); // add a custom group in pivot groupby - await click(pivot.el, ".o_dropdown_menu .o_add_custom_group_menu .o_dropdown_toggler"); + await mouseEnter(pivot.el, ".dropdown-menu .o_add_custom_group_menu .dropdown-toggle"); pivot.el.querySelector(".o_add_custom_group_menu select").value = "customer"; await triggerEvent( pivot.el, - ".o_dropdown_menu .o_add_custom_group_menu select", + ".dropdown-menu .o_add_custom_group_menu select", "change" ); - await click( - pivot.el, - ".o_dropdown_menu .o_add_custom_group_menu .o_dropdown_menu .btn" - ); + await click(pivot.el, ".dropdown-menu .o_add_custom_group_menu .dropdown-menu .btn"); await click(pivot.el.querySelectorAll("tbody .o_pivot_header_cell_closed")[1]); items = pivot.el.querySelectorAll(".o_menu_item"); @@ -1211,7 +1207,7 @@ QUnit.module("Views", (hooks) => { ); assert.containsN( pivot, - "tbody .o_dropdown_menu .dropdown-divider", + "tbody .dropdown-menu .dropdown-divider", 2, "pivot groupby menu should now have two separators" ); @@ -1261,9 +1257,9 @@ QUnit.module("Views", (hooks) => { // click on closed header to open dropdown and apply groupby on date field await click(pivot.el.querySelector("thead .o_pivot_header_cell_closed")); - await click( + await mouseEnter( pivot.el.querySelector( - "thead .o_dropdown_menu .o_add_custom_group_menu .o_dropdown_toggler " + "thead .dropdown-menu .o_add_custom_group_menu .dropdown-toggle " ) ); @@ -1291,25 +1287,25 @@ QUnit.module("Views", (hooks) => { await toggleGroupByMenu(pivot); assert.containsNone( pivot, - ".o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_menu_item", + ".o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item", "should not have any dropdown item in searchview groupby" ); assert.containsOnce( pivot, - ".o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_add_custom_group_menu", + ".o_control_panel .o_cp_bottom_right .dropdown-menu .o_add_custom_group_menu", "should have add custom group item in searchview groupby" ); // click on closed header to open dropdown await click(pivot.el.querySelectorAll("tbody .o_pivot_header_cell_closed")[1]); assert.containsN( pivot, - "tbody .o_dropdown_menu .o_menu_item", + "tbody .dropdown-menu .o_menu_item", 6, "should have 6 dropdown items i.e. all groupable fields available" ); assert.containsOnce( pivot, - ".o_dropdown_menu .o_add_custom_group_menu", + ".dropdown-menu .o_add_custom_group_menu", "should have custom group generator in groupby dropdown" ); }); @@ -1361,12 +1357,12 @@ QUnit.module("Views", (hooks) => { await toggleGroupByMenu(pivot); assert.containsOnce( pivot, - ".o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_menu_item", + ".o_control_panel .o_cp_bottom_right .dropdown-menu .o_menu_item", "should have 1 dropdown item in searchview groupby" ); assert.containsNone( pivot, - ".o_control_panel .o_cp_bottom_right .o_dropdown_menu .o_add_custom_group_menu", + ".o_control_panel .o_cp_bottom_right .dropdown-menu .o_add_custom_group_menu", "should not have custom group generator in searchview groupby" ); @@ -1374,12 +1370,12 @@ QUnit.module("Views", (hooks) => { await click(pivot.el.querySelector("tbody .o_pivot_header_cell_closed")); assert.containsOnce( pivot, - "tbody .o_dropdown_menu .o_dropdown_item", + "tbody .dropdown-menu .dropdown-item", "should have 1 dropdown items" ); assert.containsNone( pivot, - ".o_dropdown_menu .o_add_custom_group_menu", + ".dropdown-menu .o_add_custom_group_menu", "should not have custom group generator in groupby dropdown" ); } @@ -1407,24 +1403,24 @@ QUnit.module("Views", (hooks) => { // click on closed header to open dropdown await click(pivot.el.querySelector("thead .o_pivot_header_cell_closed")); - assert.containsOnce(pivot, "thead .o_dropdown_menu .o_add_custom_group_menu"); - await click( + assert.containsOnce(pivot, "thead .dropdown-menu .o_add_custom_group_menu"); + await mouseEnter( pivot.el.querySelector( - "thead .o_dropdown_menu .o_add_custom_group_menu .o_dropdown_toggler" + "thead .dropdown-menu .o_add_custom_group_menu .dropdown-toggle" ) ); assert.containsOnce( pivot, - "thead .o_dropdown_menu .o_add_custom_group_menu .o_dropdown_menu" + "thead .dropdown-menu .o_add_custom_group_menu .dropdown-menu" ); // click on apply button should close dropdown await click( pivot.el.querySelector( - "thead .o_dropdown_menu .o_add_custom_group_menu .o_dropdown_menu .btn-primary" + "thead .dropdown-menu .o_add_custom_group_menu .dropdown-menu .btn-primary" ) ); - assert.containsNone(pivot, "thead .o_dropdown_menu"); + assert.containsNone(pivot, "thead .dropdown-menu"); } ); @@ -1452,16 +1448,16 @@ QUnit.module("Views", (hooks) => { "should have 3 cells: 1 for the open header, and 2 for data" ); - await click(pivot.el.querySelector(".o_cp_bottom_left button.o_dropdown_toggler")); + await click(pivot.el.querySelector(".o_cp_bottom_left button.dropdown-toggle")); assert.doesNotHaveClass( - $(pivot.el).find(".o_dropdown_item:contains(Count)"), + $(pivot.el).find(".dropdown-item:contains(Count)"), "selected", "the __count measure should not be selected" ); - await click($(pivot.el).find(".o_cp_bottom_left .o_dropdown_item:contains(Count)")[0]); + await click($(pivot.el).find(".o_cp_bottom_left .dropdown-item:contains(Count)")[0]); assert.hasClass( - $(pivot.el).find(".o_cp_bottom_left .o_dropdown_item:contains(Count)"), + $(pivot.el).find(".o_cp_bottom_left .dropdown-item:contains(Count)"), "selected", "the __count measure should be selected" ); @@ -1473,10 +1469,10 @@ QUnit.module("Views", (hooks) => { ); assert.strictEqual(rpcCount, 2, "should have done 2 rpcs to reload data"); - await click($(pivot.el).find(".o_cp_bottom_left .o_dropdown_item:contains(Count)")[0]); + await click($(pivot.el).find(".o_cp_bottom_left .dropdown-item:contains(Count)")[0]); assert.doesNotHaveClass( - $(pivot.el).find(".o_dropdown_item:contains(Count)")[0], + $(pivot.el).find(".dropdown-item:contains(Count)")[0], "selected", "the __count measure should not be selected" ); @@ -1500,8 +1496,8 @@ QUnit.module("Views", (hooks) => { assert.containsNone(pivot, ".o_view_nocontent"); assert.containsOnce(pivot, "table"); - await click(pivot.el.querySelector(".o_cp_bottom_left button.o_dropdown_toggler")); - await click($(pivot.el).find(".o_cp_bottom_left .o_dropdown_item:contains(Count)")[0]); + await click(pivot.el.querySelector(".o_cp_bottom_left button.dropdown-toggle")); + await click($(pivot.el).find(".o_cp_bottom_left .dropdown-item:contains(Count)")[0]); assert.containsOnce(pivot, ".o_view_nocontent"); assert.containsNone(pivot, "table"); @@ -1577,7 +1573,7 @@ QUnit.module("Views", (hooks) => { // tries to open a field selection menu, to make sure it was not // removed from the dom. await click(pivot.el.querySelector("tbody .o_pivot_header_cell_closed")); - assert.containsOnce(pivot, "tbody .o_dropdown_menu"); + assert.containsOnce(pivot, "tbody .dropdown-menu"); }); QUnit.test("tries to restore previous state after domain change", async function (assert) { @@ -1927,7 +1923,7 @@ QUnit.module("Views", (hooks) => { // expand header on field customer await click(pivot.el.querySelectorAll("thead .o_pivot_header_cell_closed")[1]); - await click(pivot.el.querySelectorAll("thead .o_dropdown_menu .o_dropdown_item")[1]); + await click(pivot.el.querySelectorAll("thead .dropdown-menu .dropdown-item")[1]); expectedContext = { group_by: [], pivot_column_groupby: ["date:day", "customer"], @@ -1941,7 +1937,7 @@ QUnit.module("Views", (hooks) => { // expand row on field product_id await click(pivot.el.querySelector("tbody .o_pivot_header_cell_closed")); - await click(pivot.el.querySelectorAll("tbody .o_dropdown_menu .o_dropdown_item")[4]); + await click(pivot.el.querySelectorAll("tbody .dropdown-menu .dropdown-item")[4]); expectedContext = { group_by: [], pivot_column_groupby: ["date:day", "customer"], @@ -2042,7 +2038,7 @@ QUnit.module("Views", (hooks) => { // Group row by product_id await click(pivot.el, "tbody .o_pivot_header_cell_closed"); - await click(pivot.el, ".o_dropdown_menu li:nth-child(5)"); + await click(pivot.el, ".dropdown-menu span:nth-child(5)"); expectedContext = { group_by: [], pivot_column_groupby: [], @@ -2056,7 +2052,7 @@ QUnit.module("Views", (hooks) => { // Group column by customer await click(pivot.el, "thead .o_pivot_header_cell_closed"); - await click(pivot.el, ".o_dropdown_menu li:nth-child(2)"); + await click(pivot.el, ".dropdown-menu span:nth-child(2)"); expectedContext = { group_by: [], pivot_column_groupby: ["customer"], @@ -2214,7 +2210,7 @@ QUnit.module("Views", (hooks) => { // Group row by product_id await click(webClient.el, "tbody .o_pivot_header_cell_closed"); - await click(webClient.el, ".o_dropdown_menu li:nth-child(5)"); + await click(webClient.el, ".dropdown-menu span:nth-child(5)"); assert.strictEqual( webClient.el.querySelector("tbody .o_pivot_header_cell_closed").textContent, @@ -2507,7 +2503,7 @@ QUnit.module("Views", (hooks) => { // Set a column groupby await click(pivot.el.querySelector("thead .o_pivot_header_cell_closed")); - await click(pivot.el.querySelectorAll("thead .o_dropdown_item")[1]); + await click(pivot.el.querySelectorAll("thead .dropdown-item")[1]); // Set a domain await toggleFilterMenu(pivot); @@ -2528,7 +2524,7 @@ QUnit.module("Views", (hooks) => { // Set a column groupby await removeFacet(pivot); // remove previously saved favorite await click(pivot.el.querySelector("thead .o_pivot_header_cell_closed")); - await click(pivot.el.querySelectorAll("thead .o_dropdown_menu .o_dropdown_item")[4]); + await click(pivot.el.querySelectorAll("thead .dropdown-menu .dropdown-item")[4]); // Set a domain await toggleFilterMenu(pivot); @@ -2570,14 +2566,14 @@ QUnit.module("Views", (hooks) => { // expand a col group await click(pivot.el.querySelectorAll("thead .o_pivot_header_cell_closed")[1]); - await click(pivot.el.querySelectorAll("thead .o_dropdown_menu .o_dropdown_item")[1]); + await click(pivot.el.querySelectorAll("thead .dropdown-menu .dropdown-item")[1]); values = ["29", "1", "2", "32", "12", "12", "17", "1", "2", "20"]; assert.strictEqual(getCurrentValues(pivot), values.join(",")); // expand a row group await click(pivot.el.querySelectorAll("tbody .o_pivot_header_cell_closed")[1]); - await click(pivot.el.querySelectorAll("tbody .o_dropdown_menu .o_dropdown_item")[3]); + await click(pivot.el.querySelectorAll("tbody .dropdown-menu .dropdown-item")[3]); values = ["29", "1", "2", "32", "12", "12", "17", "1", "2", "20", "17", "1", "2", "20"]; assert.strictEqual(getCurrentValues(pivot), values.join(",")); @@ -2644,7 +2640,7 @@ QUnit.module("Views", (hooks) => { // Set a column groupby await click(pivot.el.querySelector("thead .o_pivot_header_cell_closed")); - await click(pivot.el.querySelectorAll("thead .o_dropdown_menu .o_dropdown_item")[1]); + await click(pivot.el.querySelectorAll("thead .dropdown-menu .dropdown-item")[1]); assert.containsOnce(pivot, "table"); @@ -2750,9 +2746,9 @@ QUnit.module("Views", (hooks) => { }); await click(pivot.el.querySelector("thead .o_pivot_header_cell_closed")); - await click(pivot.el.querySelector("thead .o_dropdown_menu .o_dropdown_toggler")); + await mouseEnter(pivot.el.querySelector("thead .dropdown-menu .dropdown-toggle")); await click( - pivot.el.querySelectorAll("thead .o_dropdown_menu .o_dropdown_menu .o_dropdown_item")[3] + pivot.el.querySelectorAll("thead .dropdown-menu .dropdown-menu .dropdown-item")[3] ); // close and reopen row groupings after changing value @@ -2760,14 +2756,14 @@ QUnit.module("Views", (hooks) => { await click(pivot.el.querySelector("tbody .o_pivot_header_cell_opened")); await click(pivot.el.querySelector("tbody .o_pivot_header_cell_closed")); - await click(pivot.el.querySelectorAll("tbody .o_dropdown_menu .o_dropdown_item")[4]); + await click(pivot.el.querySelectorAll("tbody .dropdown-menu .dropdown-item")[4]); assert.strictEqual(pivot.el.querySelectorAll(".o_pivot_cell_value")[4].innerText, ""); // xphone December 2016 // invert axis, and reopen column groupings await click(pivot.el.querySelector(".o_cp_bottom_left .o_pivot_flip_button")); await click(pivot.el.querySelector("thead .o_pivot_header_cell_opened")); await click(pivot.el.querySelector("thead .o_pivot_header_cell_closed")); - await click(pivot.el.querySelectorAll("thead .o_dropdown_menu .o_dropdown_item")[4]); + await click(pivot.el.querySelectorAll("thead .dropdown-menu .dropdown-item")[4]); assert.strictEqual(pivot.el.querySelectorAll(".o_pivot_cell_value")[3].innerText, ""); // December 2016 xphone }); @@ -3103,13 +3099,13 @@ QUnit.module("Views", (hooks) => { }, }); - await click(pivot.el.querySelector(".o_cp_bottom_left button.o_dropdown_toggler")); - assert.containsOnce(pivot, ".o_cp_bottom_left .o_dropdown_item"); + await click(pivot.el.querySelector(".o_cp_bottom_left button.dropdown-toggle")); + assert.containsOnce(pivot, ".o_cp_bottom_left .dropdown-item"); assert.strictEqual( - pivot.el.querySelector(".o_cp_bottom_left .o_dropdown_item").innerText, + pivot.el.querySelector(".o_cp_bottom_left .dropdown-item").innerText, "Count" ); - assert.hasClass(pivot.el.querySelector(".o_cp_bottom_left .o_dropdown_item"), "selected"); + assert.hasClass(pivot.el.querySelector(".o_cp_bottom_left .dropdown-item"), "selected"); }); QUnit.test("not use a many2one as a measure by default", async function (assert) { @@ -3125,13 +3121,13 @@ QUnit.module("Views", (hooks) => { `, }); - await click(pivot.el.querySelector(".o_cp_bottom_left button.o_dropdown_toggler")); - assert.containsOnce(pivot, ".o_cp_bottom_left .o_dropdown_item"); + await click(pivot.el.querySelector(".o_cp_bottom_left button.dropdown-toggle")); + assert.containsOnce(pivot, ".o_cp_bottom_left .dropdown-item"); assert.strictEqual( - pivot.el.querySelector(".o_cp_bottom_left .o_dropdown_item").innerText, + pivot.el.querySelector(".o_cp_bottom_left .dropdown-item").innerText, "Count" ); - assert.hasClass(pivot.el.querySelector(".o_cp_bottom_left .o_dropdown_item"), "selected"); + assert.hasClass(pivot.el.querySelector(".o_cp_bottom_left .dropdown-item"), "selected"); }); QUnit.test( @@ -3150,14 +3146,14 @@ QUnit.module("Views", (hooks) => { `, additionalMeasures: ["product_id"], }); - await click(pivot.el.querySelector(".o_cp_bottom_left button.o_dropdown_toggler")); - assert.containsN(pivot, ".o_cp_bottom_left .o_dropdown_item", 2); + await click(pivot.el.querySelector(".o_cp_bottom_left button.dropdown-toggle")); + assert.containsN(pivot, ".o_cp_bottom_left .dropdown-item", 2); assert.strictEqual( - pivot.el.querySelector(".o_cp_bottom_left .o_dropdown_item").innerText, + pivot.el.querySelector(".o_cp_bottom_left .dropdown-item").innerText, "Product" ); assert.doesNotHaveClass( - pivot.el.querySelector(".o_cp_bottom_left .o_dropdown_item"), + pivot.el.querySelector(".o_cp_bottom_left .dropdown-item"), "selected" ); } @@ -3198,9 +3194,9 @@ QUnit.module("Views", (hooks) => { }); await click(pivot.el.querySelector("tbody .o_pivot_header_cell_closed")); // click on date by month - await click(pivot.el.querySelector("tbody .o_dropdown_menu .o_dropdown_toggler")); + await mouseEnter(pivot.el.querySelector("tbody .dropdown-menu .dropdown-toggle")); await click( - pivot.el.querySelectorAll("tbody .o_dropdown_menu .o_dropdown_menu .o_dropdown_item")[3] + pivot.el.querySelectorAll("tbody .dropdown-menu .dropdown-menu .dropdown-item")[3] ); assert.strictEqual( @@ -3226,8 +3222,8 @@ QUnit.module("Views", (hooks) => { additionalMeasures: ["product_id"], }); - await click(pivot.el.querySelector(".o_cp_bottom_left button.o_dropdown_toggler")); - await click(pivot.el.querySelector(".o_cp_bottom_left .o_dropdown_item")); + await click(pivot.el.querySelector(".o_cp_bottom_left button.dropdown-toggle")); + await click(pivot.el.querySelector(".o_cp_bottom_left .dropdown-item")); assert.strictEqual( [...pivot.el.querySelectorAll(".o_pivot_cell_value")] .map((c) => c.innerText) @@ -3254,7 +3250,7 @@ QUnit.module("Views", (hooks) => { }); await click(pivot.el.querySelector("tbody .o_pivot_header_cell_closed")); - await click(pivot.el.querySelectorAll("tbody .o_dropdown_item")[4]); + await click(pivot.el.querySelectorAll("tbody .dropdown-item")[4]); assert.strictEqual( [...pivot.el.querySelectorAll(".o_pivot_cell_value")] @@ -3297,11 +3293,11 @@ QUnit.module("Views", (hooks) => { // Set a column groupby await click(pivot.el.querySelector("thead .o_pivot_header_cell_closed")); - await click(pivot.el.querySelectorAll("thead .o_dropdown_item")[1]); + await click(pivot.el.querySelectorAll("thead .dropdown-item")[1]); // Set a Row groupby await click(pivot.el.querySelector("tbody .o_pivot_header_cell_closed")); - await click(pivot.el.querySelectorAll("tbody .o_dropdown_item")[4]); + await click(pivot.el.querySelectorAll("tbody .dropdown-item")[4]); // Add a filter await toggleFilterMenu(pivot); @@ -3404,9 +3400,9 @@ QUnit.module("Views", (hooks) => { `, }); - await click(pivot.el.querySelector(".o_cp_bottom_left button.o_dropdown_toggler")); + await click(pivot.el.querySelector(".o_cp_bottom_left button.dropdown-toggle")); assert.strictEqual( - [...pivot.el.querySelectorAll(".o_cp_bottom_left .o_dropdown_item")] + [...pivot.el.querySelectorAll(".o_cp_bottom_left .dropdown-item")] .map((i) => i.innerText) .join(""), "bouhFoomoddZipCount" @@ -3547,7 +3543,7 @@ QUnit.module("Views", (hooks) => { // with data, with row groupby await click(pivot.el.querySelector("tbody .o_pivot_header_cell_closed")); - await click(pivot.el.querySelectorAll("tbody .o_dropdown_menu .o_dropdown_item")[4]); + await click(pivot.el.querySelectorAll("tbody .dropdown-menu .dropdown-item")[4]); values = [ "19", "0", @@ -3576,12 +3572,12 @@ QUnit.module("Views", (hooks) => { ]; assert.strictEqual(getCurrentValues(pivot), values.join()); - await click(pivot.el.querySelector(".o_cp_bottom_left button.o_dropdown_toggler")); + await click(pivot.el.querySelector(".o_cp_bottom_left button.dropdown-toggle")); await click( - pivot.el.querySelectorAll(".o_cp_bottom_left .o_dropdown_menu .o_dropdown_item")[0] + pivot.el.querySelectorAll(".o_cp_bottom_left .dropdown-menu .dropdown-item")[0] ); await click( - pivot.el.querySelectorAll(".o_cp_bottom_left .o_dropdown_menu .o_dropdown_item")[1] + pivot.el.querySelectorAll(".o_cp_bottom_left .dropdown-menu .dropdown-item")[1] ); values = [ "1", @@ -3612,10 +3608,10 @@ QUnit.module("Views", (hooks) => { assert.strictEqual(getCurrentValues(pivot), values.join()); await click( - pivot.el.querySelectorAll(".o_cp_bottom_left .o_dropdown_menu .o_dropdown_item")[2] + pivot.el.querySelectorAll(".o_cp_bottom_left .dropdown-menu .dropdown-item")[2] ); await click( - pivot.el.querySelectorAll(".o_cp_bottom_left .o_dropdown_menu .o_dropdown_item")[1] + pivot.el.querySelectorAll(".o_cp_bottom_left .dropdown-menu .dropdown-item")[1] ); values = [ "2", @@ -4085,21 +4081,21 @@ QUnit.module("Views", (hooks) => { arch: ``, }); - assert.containsNone(pivot, ".o_cp_bottom_left .o_dropdown_menu"); + assert.containsNone(pivot, ".o_cp_bottom_left .dropdown-menu"); // open the "Measures" menu - await click(pivot.el.querySelector(".o_cp_bottom_left .o_dropdown_toggler")); - assert.containsOnce(pivot, ".o_cp_bottom_left .o_dropdown_menu"); + await click(pivot.el.querySelector(".o_cp_bottom_left .dropdown-toggle")); + assert.containsOnce(pivot, ".o_cp_bottom_left .dropdown-menu"); // click on the divider in the "Measures" menu does not crash - await click(pivot.el.querySelector(".o_cp_bottom_left .o_dropdown_menu .dropdown-divider")); + await click(pivot.el.querySelector(".o_cp_bottom_left .dropdown-menu .dropdown-divider")); // the menu should still be open - assert.containsOnce(pivot, ".o_cp_bottom_left .o_dropdown_menu"); + assert.containsOnce(pivot, ".o_cp_bottom_left .dropdown-menu"); // click on the measure list but not on a menu item or the separator - await click(pivot.el.querySelector(".o_cp_bottom_left .o_dropdown_menu")); + await click(pivot.el.querySelector(".o_cp_bottom_left .dropdown-menu")); // the menu should still be open - assert.containsOnce(pivot, ".o_cp_bottom_left .o_dropdown_menu"); + assert.containsOnce(pivot, ".o_cp_bottom_left .dropdown-menu"); }); QUnit.test( @@ -4523,7 +4519,7 @@ QUnit.module("Views", (hooks) => { // Set a Row groupby await click(pivot.el, "tbody tr:nth-child(2) .o_pivot_header_cell_closed"); - await click(pivot.el, "tbody .o_dropdown_menu .o_menu_item:nth-child(5)"); + await click(pivot.el, "tbody .dropdown-menu .o_menu_item:nth-child(5)"); assert.deepEqual( [...pivot.el.querySelectorAll("thead th")].map((th) => th.innerText), @@ -4571,7 +4567,7 @@ QUnit.module("Views", (hooks) => { // add a col groupby on Product await click(pivot.el.querySelector("thead th.o_pivot_header_cell_closed")); - await click(pivot.el.querySelectorAll("thead .o_dropdown_menu .o_dropdown_item")[5]); + await click(pivot.el.querySelectorAll("thead .dropdown-menu .dropdown-item")[5]); // Click on the two header dropdown togglers await click(pivot.el.querySelectorAll("thead th.o_pivot_header_cell_closed")[0]); @@ -4579,7 +4575,7 @@ QUnit.module("Views", (hooks) => { assert.containsOnce( pivot, - "thead .o_dropdown_menu", + "thead .dropdown-menu", "Only one dropdown should be displayed at a time" ); }); @@ -4848,7 +4844,7 @@ QUnit.module("Views", (hooks) => { // drill down first row group (group by company_type) await click(webClient.el.querySelector("tbody .o_pivot_header_cell_closed")); - await click(webClient.el.querySelector(".o_pivot .o_dropdown_menu .o_dropdown_item")); + await click(webClient.el.querySelector(".o_pivot .dropdown-menu .dropdown-item")); assert.strictEqual(getCurrentValues(webClient), ["4", "2", "1", "1", "2"].join(",")); @@ -4947,7 +4943,7 @@ QUnit.module("Views", (hooks) => { // drill down first row group (group by company_type) await click(webClient.el.querySelector("tbody .o_pivot_header_cell_closed")); - await click(webClient.el.querySelector(".o_pivot .o_dropdown_menu .o_dropdown_item")); + await click(webClient.el.querySelector(".o_pivot .dropdown-menu .dropdown-item")); assert.strictEqual(getCurrentValues(webClient), ["32", "12", "12", "20"].join(",")); @@ -5156,7 +5152,7 @@ QUnit.module("Views", (hooks) => { // Add a groupby (this operation should be ignored as it concerns the old // table, which will be replaced soon) await click(pivot.el.querySelector("thead .o_pivot_header_cell_closed")); - await click(pivot.el.querySelector("thead .o_dropdown_menu .o_dropdown_item")); + await click(pivot.el.querySelector("thead .dropdown-menu .dropdown-item")); assert.strictEqual(getCurrentValues(pivot), ["32", "12", "20"].join(",")); def.resolve(); @@ -5189,7 +5185,7 @@ QUnit.module("Views", (hooks) => { // Add a groupby, to have a group to expand afterwards await click(pivot.el.querySelector("tbody .o_pivot_header_cell_closed")); - await click(pivot.el.querySelector("tbody .o_dropdown_menu .o_dropdown_item")); + await click(pivot.el.querySelector("tbody .dropdown-menu .dropdown-item")); assert.strictEqual(getCurrentValues(pivot), ["32", "12", "12", "20"].join(",")); @@ -5331,12 +5327,12 @@ QUnit.module("Views", (hooks) => { // "Count" is the only measure available assert.deepEqual( - [...pivot.el.querySelectorAll(".o_cp_bottom_left .o_dropdown_menu li")].map( + [...pivot.el.querySelectorAll(".o_cp_bottom_left .dropdown-menu .o_menu_item")].map( (e) => e.innerText ), ["Count"] ); // No separator should be displayed in the menu "Measures" - assert.containsNone(pivot, ".o_cp_bottom_left .o_dropdown_menu div.dropdown-divider"); + assert.containsNone(pivot, ".o_cp_bottom_left .dropdown-menu div.dropdown-divider"); }); }); diff --git a/addons/web/static/tests/webclient/actions/load_state_tests.js b/addons/web/static/tests/webclient/actions/load_state_tests.js index c93e1aec3c8..f9cd51b5cb2 100644 --- a/addons/web/static/tests/webclient/actions/load_state_tests.js +++ b/addons/web/static/tests/webclient/actions/load_state_tests.js @@ -967,8 +967,8 @@ QUnit.module("ActionManager", (hooks) => { assert.containsOnce(webClient, ".o_dialog_error"); await click(webClient.el, ".modal-header .close"); assert.containsNone(webClient, ".o_dialog_error"); - await click(webClient.el, "nav .o_navbar_apps_menu .o_dropdown_toggler "); - assert.containsN(webClient, ".o_dropdown_item.o_app", 3); + await click(webClient.el, "nav .o_navbar_apps_menu .dropdown-toggle "); + assert.containsN(webClient, ".dropdown-item.o_app", 3); assert.containsNone(webClient, ".o_menu_brand"); assert.strictEqual(webClient.el.querySelector(".o_action_manager").innerHTML, ""); assert.deepEqual(webClient.env.services.router.current.hash, { diff --git a/addons/web/static/tests/webclient/actions/push_state_tests.js b/addons/web/static/tests/webclient/actions/push_state_tests.js index e9176dc6cb5..1ebddc0244d 100644 --- a/addons/web/static/tests/webclient/actions/push_state_tests.js +++ b/addons/web/static/tests/webclient/actions/push_state_tests.js @@ -25,7 +25,7 @@ QUnit.module("ActionManager", (hooks) => { let urlState = webClient.env.services.router.current; assert.deepEqual(urlState.hash, {}); await click(webClient.el, ".o_navbar_apps_menu button"); - await click(webClient.el, ".o_navbar_apps_menu .o_dropdown_item:nth-child(3)"); + await click(webClient.el, ".o_navbar_apps_menu .dropdown-item:nth-child(3)"); await nextTick(); await nextTick(); urlState = webClient.env.services.router.current; @@ -44,7 +44,7 @@ QUnit.module("ActionManager", (hooks) => { let urlState = webClient.env.services.router.current; assert.deepEqual(urlState.hash, {}); await click(webClient.el, ".o_navbar_apps_menu button"); - await click(webClient.el, ".o_navbar_apps_menu .o_dropdown_item:nth-child(3)"); + await click(webClient.el, ".o_navbar_apps_menu .dropdown-item:nth-child(3)"); await nextTick(); await nextTick(); urlState = webClient.env.services.router.current; @@ -147,7 +147,7 @@ QUnit.module("ActionManager", (hooks) => { await doAction(webClient, "client_action_pushes"); await click(webClient.el, ".test_client_action"); await click(webClient.el, ".o_navbar_apps_menu button"); - await click(webClient.el, ".o_navbar_apps_menu .o_dropdown_item:nth-child(3)"); + await click(webClient.el, ".o_navbar_apps_menu .dropdown-item:nth-child(3)"); await nextTick(); await nextTick(); urlState = webClient.env.services.router.current; 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 baa33cae188..563541b3716 100644 --- a/addons/web/static/tests/webclient/actions/window_action_tests.js +++ b/addons/web/static/tests/webclient/actions/window_action_tests.js @@ -75,14 +75,10 @@ QUnit.module("ActionManager", (hooks) => { assert.containsNone(webClient, ".o_cp_action_menus"); await testUtils.dom.clickFirst($(webClient.el).find("input.custom-control-input")); assert.isVisible( - $(webClient.el).find( - '.o_cp_action_menus button.o_dropdown_toggler:contains("Print")' - )[0] + $(webClient.el).find('.o_cp_action_menus button.dropdown-toggle:contains("Print")')[0] ); assert.isVisible( - $(webClient.el).find( - '.o_cp_action_menus button.o_dropdown_toggler:contains("Action")' - )[0] + $(webClient.el).find('.o_cp_action_menus button.dropdown-toggle:contains("Action")')[0] ); }); @@ -1356,7 +1352,7 @@ QUnit.module("ActionManager", (hooks) => { $(webClient.el).find(".o_control_panel .o_cp_bottom_right button:contains(Group By)") ); await testUtils.dom.click( - $(webClient.el).find(".o_control_panel .o_group_by_menu li:first") + $(webClient.el).find(".o_control_panel .o_group_by_menu .o_menu_item:first") ); await legacyExtraNextTick(); assert.containsN( @@ -1485,6 +1481,7 @@ QUnit.module("ActionManager", (hooks) => { }, }, }; + patchWithCleanup(browser, { setTimeout: (fn) => fn() }); const webClient = await createWebClient({ serverData, legacyParams }); await doAction(webClient, 33); assert.containsN(webClient, ".o_data_row", 5, "should contain 5 records"); @@ -1575,19 +1572,19 @@ QUnit.module("ActionManager", (hooks) => { const webClient = await createWebClient({ serverData }); await doAction(webClient, 1); assert.isVisible( - webClient.el.querySelector(".o_search_options .o_dropdown.o_filter_menu"), + webClient.el.querySelector(".o_search_options .dropdown.o_filter_menu"), "the search options should be available" ); await doAction(webClient, 3); assert.isVisible( - webClient.el.querySelector(".o_search_options .o_dropdown.o_filter_menu"), + webClient.el.querySelector(".o_search_options .dropdown.o_filter_menu"), "the search options should be available" ); // go back using the breadcrumbs await testUtils.dom.click($(webClient.el).find(".o_control_panel .breadcrumb a:first")); await legacyExtraNextTick(); assert.isVisible( - webClient.el.querySelector(".o_search_options .o_dropdown.o_filter_menu"), + webClient.el.querySelector(".o_search_options .dropdown.o_filter_menu"), "the search options should be available" ); } @@ -2278,12 +2275,12 @@ QUnit.module("ActionManager", (hooks) => { await doAction(webClient, 1); assert.containsNone( webClient.el, - ".o_debug_manager .o_dropdown_item:contains('Edit View: Kanban')" + ".o_debug_manager .dropdown-item:contains('Edit View: Kanban')" ); - await click(webClient.el.querySelector(".o_debug_manager .o_dropdown_toggler")); + await click(webClient.el.querySelector(".o_debug_manager .dropdown-toggle")); assert.containsOnce( webClient.el, - ".o_debug_manager .o_dropdown_item:contains('Edit View: Kanban')" + ".o_debug_manager .dropdown-item:contains('Edit View: Kanban')" ); }); diff --git a/addons/web/static/tests/webclient/navbar_tests.js b/addons/web/static/tests/webclient/navbar_tests.js index b34c948ae4d..3d1ab59a4f7 100644 --- a/addons/web/static/tests/webclient/navbar_tests.js +++ b/addons/web/static/tests/webclient/navbar_tests.js @@ -47,7 +47,7 @@ QUnit.test("can be rendered", async (assert) => { const navbar = await mount(NavBar, { env, target }); assert.containsOnce( navbar.el, - ".o_navbar_apps_menu button.o_dropdown_toggler", + ".o_navbar_apps_menu button.dropdown-toggle", "1 apps menu toggler present" ); navbar.destroy(); @@ -58,10 +58,10 @@ QUnit.test("dropdown menu can be toggled", async (assert) => { const target = getFixture(); const navbar = await mount(NavBar, { env, target }); const dropdown = navbar.el.querySelector(".o_navbar_apps_menu"); - await click(dropdown, "button.o_dropdown_toggler"); - assert.containsOnce(dropdown, "ul.o_dropdown_menu"); - await click(dropdown, "button.o_dropdown_toggler"); - assert.containsNone(dropdown, "ul.o_dropdown_menu"); + await click(dropdown, "button.dropdown-toggle"); + assert.containsOnce(dropdown, ".dropdown-menu"); + await click(dropdown, "button.dropdown-toggle"); + assert.containsNone(dropdown, ".dropdown-menu"); navbar.destroy(); }); @@ -80,8 +80,8 @@ QUnit.test("data-menu-xmlid attribute on AppsMenu items", async (assert) => { // check apps const appsMenu = navbar.el.querySelector(".o_navbar_apps_menu"); - await click(appsMenu, "button.o_dropdown_toggler"); - const menuItems = appsMenu.querySelectorAll("li"); + await click(appsMenu, "button.dropdown-toggle"); + const menuItems = appsMenu.querySelectorAll("a"); assert.strictEqual( menuItems[0].dataset.menuXmlid, "wowl", @@ -96,17 +96,14 @@ QUnit.test("data-menu-xmlid attribute on AppsMenu items", async (assert) => { // check menus env.services.menu.setCurrentMenu(1); await nextTick(); - assert.containsOnce(navbar, ".o_menu_sections .o_dropdown_item[data-menu-xmlid=menu_3]"); + assert.containsOnce(navbar, ".o_menu_sections .dropdown-item[data-menu-xmlid=menu_3]"); // check sub menus toggler - assert.containsOnce( - navbar, - ".o_menu_sections button.o_dropdown_toggler[data-menu-xmlid=menu_4]" - ); + assert.containsOnce(navbar, ".o_menu_sections button.dropdown-toggle[data-menu-xmlid=menu_4]"); // check sub menus - await click(navbar.el.querySelector(".o_menu_sections .o_dropdown_toggler")); - assert.containsOnce(navbar, ".o_menu_sections .o_dropdown_item[data-menu-xmlid=menu_5]"); + await click(navbar.el.querySelector(".o_menu_sections .dropdown-toggle")); + assert.containsOnce(navbar, ".o_menu_sections .dropdown-item[data-menu-xmlid=menu_5]"); navbar.destroy(); }); @@ -117,10 +114,10 @@ QUnit.test("navbar can display current active app", async (assert) => { const navbar = await mount(NavBar, { env, target }); const dropdown = navbar.el.querySelector(".o_navbar_apps_menu"); // Open apps menu - await click(dropdown, "button.o_dropdown_toggler"); + await click(dropdown, "button.dropdown-toggle"); assert.containsOnce( dropdown, - "ul.o_dropdown_menu li.o_dropdown_item:not(.focus)", + ".dropdown-menu .dropdown-item:not(.focus)", "should not show the current active app as the menus service has not loaded an app yet" ); @@ -129,7 +126,7 @@ QUnit.test("navbar can display current active app", async (assert) => { await nextTick(); assert.containsOnce( dropdown, - "ul.o_dropdown_menu li.o_dropdown_item.focus", + ".dropdown-menu .dropdown-item.focus", "should show the current active app" ); navbar.destroy(); @@ -218,9 +215,9 @@ QUnit.test("can adapt with 'more' menu sections behavior", async (assert) => { "the displayed menu section should be the 'more' menu" ); // Open the more menu - await click(navbar.el, ".o_menu_sections_more .o_dropdown_toggler"); + await click(navbar.el, ".o_menu_sections_more .dropdown-toggle"); assert.deepEqual( - [...navbar.el.querySelectorAll(".o_dropdown_menu > *")].map((el) => el.textContent), + [...navbar.el.querySelectorAll(".dropdown-menu > *")].map((el) => el.textContent), ["Section 10", "Section 11", "Section 12", "Section 120", "Section 121", "Section 122"], "'more' menu should contain all hidden sections in correct order" ); @@ -401,23 +398,23 @@ QUnit.test("'more' menu sections properly updated on app change", async (assert) ); // Open the more menu - await click(navbar.el, ".o_menu_sections_more .o_dropdown_toggler"); + await click(navbar.el, ".o_menu_sections_more .dropdown-toggle"); assert.deepEqual( - [...navbar.el.querySelectorAll(".o_dropdown_menu > *")].map((el) => el.textContent), + [...navbar.el.querySelectorAll(".dropdown-menu > *")].map((el) => el.textContent), ["Section 10", "Section 11", "Section 12", "Section 120", "Section 121", "Section 122"], "'more' menu should contain App1 sections" ); // Close the more menu - await click(navbar.el, ".o_menu_sections_more .o_dropdown_toggler"); + await click(navbar.el, ".o_menu_sections_more .dropdown-toggle"); // Set App2 menu env.services.menu.setCurrentMenu(2); await nextTick(); // Open the more menu - await click(navbar.el, ".o_menu_sections_more .o_dropdown_toggler"); + await click(navbar.el, ".o_menu_sections_more .dropdown-toggle"); assert.deepEqual( - [...navbar.el.querySelectorAll(".o_dropdown_menu > *")].map((el) => el.textContent), + [...navbar.el.querySelectorAll(".dropdown-menu > *")].map((el) => el.textContent), ["Section 20", "Section 21", "Section 22", "Section 220", "Section 221", "Section 222"], "'more' menu should contain App2 sections" ); diff --git a/addons/web/static/tests/webclient/switch_company_menu_tests.js b/addons/web/static/tests/webclient/switch_company_menu_tests.js index 8901863c362..3b73626f60d 100644 --- a/addons/web/static/tests/webclient/switch_company_menu_tests.js +++ b/addons/web/static/tests/webclient/switch_company_menu_tests.js @@ -56,25 +56,25 @@ QUnit.module("SwitchCompanyMenu", (hooks) => { const scMenu = await createSwitchCompanyMenu(); - assert.strictEqual(scMenu.el.tagName.toUpperCase(), "LI"); + assert.strictEqual(scMenu.el.tagName.toUpperCase(), "DIV"); assert.hasClass(scMenu.el, "o_switch_company_menu"); assert.strictEqual(scMenu.el.textContent, "Hermit"); - await click(scMenu.el.querySelector(".o_dropdown_toggler")); + await click(scMenu.el.querySelector(".dropdown-toggle")); assert.containsN(scMenu, ".toggle_company", 3); assert.containsN(scMenu, ".log_into", 3); assert.containsOnce(scMenu.el, ".fa-check-square"); assert.containsN(scMenu.el, ".fa-square-o", 2); assert.strictEqual( - scMenu.el.querySelector(".fa-check-square").closest(".o_dropdown_item").textContent, + scMenu.el.querySelector(".fa-check-square").closest(".dropdown-item").textContent, "Hermit" ); assert.strictEqual( - scMenu.el.querySelector(".fa-square-o").closest(".o_dropdown_item").textContent, + scMenu.el.querySelector(".fa-square-o").closest(".dropdown-item").textContent, "Herman's" ); assert.strictEqual( - scMenu.el.querySelector(".o_dropdown_menu").textContent, + scMenu.el.querySelector(".dropdown-menu").textContent, "HermitHerman'sHeroes TM" ); }); @@ -96,7 +96,7 @@ QUnit.module("SwitchCompanyMenu", (hooks) => { */ assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [1]); assert.strictEqual(scMenu.env.services.company.currentCompany.id, 1); - await click(scMenu.el.querySelector(".o_dropdown_toggler")); + await click(scMenu.el.querySelector(".dropdown-toggle")); assert.containsN(scMenu.el, "[data-company-id]", 3); assert.containsN(scMenu.el, "[data-company-id] .fa-check-square", 1); assert.containsN(scMenu.el, "[data-company-id] .fa-square-o", 2); @@ -107,7 +107,7 @@ QUnit.module("SwitchCompanyMenu", (hooks) => { * [ ] Company 3 */ await click(scMenu.el.querySelectorAll(".toggle_company")[1]); - assert.containsOnce(scMenu.el, ".o_dropdown_menu", "dropdown is still opened"); + assert.containsOnce(scMenu.el, ".dropdown-menu", "dropdown is still opened"); assert.containsN(scMenu.el, "[data-company-id] .fa-check-square", 2); assert.containsN(scMenu.el, "[data-company-id] .fa-square-o", 1); await prom; @@ -131,7 +131,7 @@ QUnit.module("SwitchCompanyMenu", (hooks) => { */ assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [1]); assert.strictEqual(scMenu.env.services.company.currentCompany.id, 1); - await click(scMenu.el.querySelector(".o_dropdown_toggler")); + await click(scMenu.el.querySelector(".dropdown-toggle")); assert.containsN(scMenu.el, "[data-company-id]", 3); assert.containsN(scMenu.el, "[data-company-id] .fa-check-square", 1); assert.containsN(scMenu.el, "[data-company-id] .fa-square-o", 2); @@ -144,7 +144,7 @@ QUnit.module("SwitchCompanyMenu", (hooks) => { await click(scMenu.el.querySelectorAll(".toggle_company")[0]); await click(scMenu.el.querySelectorAll(".toggle_company")[1]); await click(scMenu.el.querySelectorAll(".toggle_company")[2]); - assert.containsOnce(scMenu.el, ".o_dropdown_menu", "dropdown is still opened"); + assert.containsOnce(scMenu.el, ".dropdown-menu", "dropdown is still opened"); assert.containsN(scMenu.el, "[data-company-id] .fa-check-square", 2); assert.containsN(scMenu.el, "[data-company-id] .fa-square-o", 1); @@ -170,7 +170,7 @@ QUnit.module("SwitchCompanyMenu", (hooks) => { */ assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [1]); assert.strictEqual(scMenu.env.services.company.currentCompany.id, 1); - await click(scMenu.el.querySelector(".o_dropdown_toggler")); + await click(scMenu.el.querySelector(".dropdown-toggle")); assert.containsN(scMenu.el, "[data-company-id]", 3); assert.containsN(scMenu.el, "[data-company-id] .fa-check-square", 1); assert.containsN(scMenu.el, "[data-company-id] .fa-square-o", 2); @@ -181,7 +181,7 @@ QUnit.module("SwitchCompanyMenu", (hooks) => { * [ ] Company 3 */ await click(scMenu.el.querySelectorAll(".toggle_company")[0]); - assert.containsOnce(scMenu.el, ".o_dropdown_menu", "dropdown is still opened"); + assert.containsOnce(scMenu.el, ".dropdown-menu", "dropdown is still opened"); assert.containsN(scMenu.el, "[data-company-id] .fa-check-square", 0); assert.containsN(scMenu.el, "[data-company-id] .fa-square-o", 3); await prom; @@ -203,7 +203,7 @@ QUnit.module("SwitchCompanyMenu", (hooks) => { */ assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [1]); assert.strictEqual(scMenu.env.services.company.currentCompany.id, 1); - await click(scMenu.el.querySelector(".o_dropdown_toggler")); + await click(scMenu.el.querySelector(".dropdown-toggle")); assert.containsN(scMenu.el, "[data-company-id]", 3); assert.containsN(scMenu.el, "[data-company-id] .fa-check-square", 1); assert.containsN(scMenu.el, "[data-company-id] .fa-square-o", 2); @@ -214,7 +214,7 @@ QUnit.module("SwitchCompanyMenu", (hooks) => { * [ ] Company 3 */ await click(scMenu.el.querySelectorAll(".log_into")[1]); - assert.containsNone(scMenu.el, ".o_dropdown_menu", "dropdown is directly closed"); + assert.containsNone(scMenu.el, ".dropdown-menu", "dropdown is directly closed"); assert.verifySteps(["cids=2"]); }); @@ -234,7 +234,7 @@ QUnit.module("SwitchCompanyMenu", (hooks) => { */ assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [3, 1]); assert.strictEqual(scMenu.env.services.company.currentCompany.id, 3); - await click(scMenu.el.querySelector(".o_dropdown_toggler")); + await click(scMenu.el.querySelector(".dropdown-toggle")); assert.containsN(scMenu.el, "[data-company-id]", 3); assert.containsN(scMenu.el, "[data-company-id] .fa-check-square", 2); assert.containsN(scMenu.el, "[data-company-id] .fa-square-o", 1); @@ -245,7 +245,7 @@ QUnit.module("SwitchCompanyMenu", (hooks) => { * [x] **Company 3** */ await click(scMenu.el.querySelectorAll(".log_into")[1]); - assert.containsNone(scMenu.el, ".o_dropdown_menu", "dropdown is directly closed"); + assert.containsNone(scMenu.el, ".dropdown-menu", "dropdown is directly closed"); assert.verifySteps(["cids=2%2C3%2C1"]); }); @@ -265,7 +265,7 @@ QUnit.module("SwitchCompanyMenu", (hooks) => { */ assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [2, 3]); assert.strictEqual(scMenu.env.services.company.currentCompany.id, 2); - await click(scMenu.el.querySelector(".o_dropdown_toggler")); + await click(scMenu.el.querySelector(".dropdown-toggle")); assert.containsN(scMenu.el, "[data-company-id]", 3); assert.containsN(scMenu.el, "[data-company-id] .fa-check-square", 2); assert.containsN(scMenu.el, "[data-company-id] .fa-square-o", 1); @@ -276,7 +276,7 @@ QUnit.module("SwitchCompanyMenu", (hooks) => { * [x] Company 3 -> log into */ await click(scMenu.el.querySelectorAll(".log_into")[2]); - assert.containsNone(scMenu.el, ".o_dropdown_menu", "dropdown is directly closed"); + assert.containsNone(scMenu.el, ".dropdown-menu", "dropdown is directly closed"); assert.verifySteps(["cids=3%2C2"]); }); @@ -295,7 +295,7 @@ QUnit.module("SwitchCompanyMenu", (hooks) => { */ assert.deepEqual(scMenu.env.services.company.allowedCompanyIds, [1]); assert.strictEqual(scMenu.env.services.company.currentCompany.id, 1); - await click(scMenu.el.querySelector(".o_dropdown_toggler")); + await click(scMenu.el.querySelector(".dropdown-toggle")); assert.containsN(scMenu.el, "[data-company-id]", 3); assert.containsN(scMenu.el, "[data-company-id] .fa-check-square", 1); assert.containsN(scMenu.el, "[data-company-id] .fa-square-o", 2); @@ -308,7 +308,7 @@ QUnit.module("SwitchCompanyMenu", (hooks) => { await click(scMenu.el.querySelectorAll(".toggle_company")[2]); await click(scMenu.el.querySelectorAll(".toggle_company")[0]); await click(scMenu.el.querySelectorAll(".log_into")[1]); - assert.containsNone(scMenu.el, ".o_dropdown_menu", "dropdown is directly closed"); + assert.containsNone(scMenu.el, ".dropdown-menu", "dropdown is directly closed"); assert.verifySteps(["cids=2"]); }); }); diff --git a/addons/web/static/tests/webclient/user_menu_tests.js b/addons/web/static/tests/webclient/user_menu_tests.js index 9824f46f98c..93e35a9ba8b 100644 --- a/addons/web/static/tests/webclient/user_menu_tests.js +++ b/addons/web/static/tests/webclient/user_menu_tests.js @@ -99,25 +99,23 @@ QUnit.test("can be rendered", async (assert) => { ); assert.containsOnce(userMenuEl, "span.oe_topbar_name"); assert.strictEqual(userMenuEl.querySelector(".oe_topbar_name").textContent, "Sauron"); - assert.containsNone(userMenuEl, "ul.o_dropdown_menu li.o_dropdown_item"); - await click(userMenu.el.querySelector("button.o_dropdown_toggler")); + assert.containsNone(userMenuEl, ".dropdown-menu .dropdown-item"); + await click(userMenu.el.querySelector("button.dropdown-toggle")); userMenuEl = userMenu.el; - assert.containsN(userMenuEl, "ul.o_dropdown_menu li.o_dropdown_item", 3); + assert.containsN(userMenuEl, ".dropdown-menu .dropdown-item", 3); assert.containsOnce(userMenuEl, "div.dropdown-divider"); - const children = [...(userMenuEl.querySelector("ul.o_dropdown_menu").children || [])]; + const children = [...(userMenuEl.querySelector(".dropdown-menu").children || [])]; assert.deepEqual( children.map((el) => el.tagName), - ["LI", "LI", "DIV", "LI"] + ["SPAN", "SPAN", "DIV", "SPAN"] ); - const items = [...userMenuEl.querySelectorAll("ul.o_dropdown_menu li.o_dropdown_item")] || []; + const items = [...userMenuEl.querySelectorAll(".dropdown-menu .dropdown-item")] || []; assert.deepEqual( items.map((el) => el.dataset.menu), ["ring", "bad", "eye"] ); - const spans = - [...userMenuEl.querySelectorAll("ul.o_dropdown_menu li.o_dropdown_item span")] || []; assert.deepEqual( - spans.map((el) => el.textContent), + items.map((el) => el.textContent), ["Ring", "Bad", "Eye"] ); for (const item of items) { @@ -165,9 +163,9 @@ QUnit.test("can execute the callback of settings", async (assert) => { env = await makeTestEnv(testConfig); userMenuRegistry.add("profile", preferencesItem); userMenu = await mount(UserMenu, { env, target }); - await click(userMenu.el.querySelector("button.o_dropdown_toggler")); - assert.containsOnce(userMenu.el, "ul.o_dropdown_menu li.o_dropdown_item"); - const item = userMenu.el.querySelector("ul.o_dropdown_menu li.o_dropdown_item"); + await click(userMenu.el.querySelector("button.dropdown-toggle")); + assert.containsOnce(userMenu.el, ".dropdown-menu .dropdown-item"); + const item = userMenu.el.querySelector(".dropdown-menu .dropdown-item"); assert.strictEqual(item.textContent, "Preferences"); await click(item); assert.verifySteps(["7", "Change My Preferences"]); diff --git a/addons/web_editor/static/lib/odoo-editor/src/style.css b/addons/web_editor/static/lib/odoo-editor/src/style.css index f54edeb88c0..f6b963a7a1a 100644 --- a/addons/web_editor/static/lib/odoo-editor/src/style.css +++ b/addons/web_editor/static/lib/odoo-editor/src/style.css @@ -136,11 +136,11 @@ .oe-toolbar li > a.dropdown-item { color: white; } -.oe-toolbar .dropdown-item:hover, .dropdown-item:focus { +.oe-toolbar .dropdown-item:hover, .oe-toolbar .dropdown-item:focus { color: white; background: #868686; } -.oe-toolbar .dropdown-item.active, .dropdown-item:active { +.oe-toolbar .dropdown-item.active, .oe-toolbar .dropdown-item:active { color: white; background: #555555; } diff --git a/addons/web_editor/static/src/scss/wysiwyg.scss b/addons/web_editor/static/src/scss/wysiwyg.scss index 438f95a881d..37777350fc0 100644 --- a/addons/web_editor/static/src/scss/wysiwyg.scss +++ b/addons/web_editor/static/src/scss/wysiwyg.scss @@ -182,7 +182,7 @@ $o-we-toolbar-color-clickable-active: $o-we-bg-darkest; .dropdown-item { &.active, &.selected { - &:not(.o_dropdown_item_active_noarrow):before { + &:not(.dropdown-item_active_noarrow):before { transform: translate(-1.5em, 0); height: 100%; display: flex; diff --git a/addons/web_tour/static/tests/debug_manager_tests.js b/addons/web_tour/static/tests/debug_manager_tests.js index c32ddba283f..f7972737fe4 100644 --- a/addons/web_tour/static/tests/debug_manager_tests.js +++ b/addons/web_tour/static/tests/debug_manager_tests.js @@ -63,10 +63,10 @@ QUnit.module("Tours", (hooks) => { const debugManager = await mount(DebugMenuParent, { env, target }); registerCleanup(() => debugManager.destroy()); - await click(debugManager.el.querySelector("button.o_dropdown_toggler")); + await click(debugManager.el.querySelector("button.dropdown-toggle")); - assert.containsOnce(debugManager.el, ".o_dropdown_item"); - await click(debugManager.el.querySelector(".o_dropdown_item")); + assert.containsOnce(debugManager.el, ".dropdown-item"); + await click(debugManager.el.querySelector(".dropdown-item")); assert.verifySteps(["consume"]); }); }); diff --git a/addons/website/static/tests/tours/dashboard_tour.js b/addons/website/static/tests/tours/dashboard_tour.js index 610adb2e5eb..343e90bf846 100644 --- a/addons/website/static/tests/tours/dashboard_tour.js +++ b/addons/website/static/tests/tours/dashboard_tour.js @@ -18,7 +18,7 @@ odoo.define("website.tour.backend_dashboard", function (require) { trigger: 'button[data-menu-xmlid="website.menu_dashboard"]', }, { - trigger: 'li[data-menu-xmlid="website.menu_website_google_analytics"]', + trigger: '.dropdown-item[data-menu-xmlid="website.menu_website_google_analytics"]', }, { // Visits section should always be present even when empty / not hooked to anything diff --git a/addons/website/views/website_navbar_templates.xml b/addons/website/views/website_navbar_templates.xml index 38adf5d4ffb..4432ac7ab43 100644 --- a/addons/website/views/website_navbar_templates.xml +++ b/addons/website/views/website_navbar_templates.xml @@ -3,14 +3,14 @@ @@ -31,22 +31,22 @@

    diff --git a/addons/website_customer/views/website_customer_templates.xml b/addons/website_customer/views/website_customer_templates.xml index 38e737c0cc3..0274fc10525 100644 --- a/addons/website_customer/views/website_customer_templates.xml +++ b/addons/website_customer/views/website_customer_templates.xml @@ -143,7 +143,7 @@ diff --git a/addons/website_event/views/website_templates.xml b/addons/website_event/views/website_templates.xml index aaebd083f5b..ce4fdaaf7b6 100644 --- a/addons/website_event/views/website_templates.xml +++ b/addons/website_event/views/website_templates.xml @@ -13,7 +13,7 @@ diff --git a/addons/website_event_exhibitor/views/event_exhibitor_templates_page.xml b/addons/website_event_exhibitor/views/event_exhibitor_templates_page.xml index 38835381866..97d2172b07a 100644 --- a/addons/website_event_exhibitor/views/event_exhibitor_templates_page.xml +++ b/addons/website_event_exhibitor/views/event_exhibitor_templates_page.xml @@ -199,7 +199,7 @@