diff --git a/addons/account/static/src/js/account_resequence_field.js b/addons/account/static/src/js/account_resequence_field.js index ec49f11fde5..cd447bc7df0 100644 --- a/addons/account/static/src/js/account_resequence_field.js +++ b/addons/account/static/src/js/account_resequence_field.js @@ -4,7 +4,7 @@ odoo.define('account.ShowResequenceRenderer', function (require) { const AbstractFieldOwl = require('web.AbstractFieldOwl'); const field_registry = require('web.field_registry_owl'); -const { Component } = owl; +const { Component, onWillUpdateProps } = owl; class ChangeLine extends Component { } ChangeLine.template = 'account.ResequenceChangeLine'; @@ -12,16 +12,15 @@ ChangeLine.props = ["changeLine", 'ordering']; class ShowResequenceRenderer extends AbstractFieldOwl { - constructor(...args) { - super(...args); + setup() { + super.setup(); this.data = this.value ? JSON.parse(this.value) : { changeLines: [], ordering: 'date', }; - } - async willUpdateProps(nextProps) { - await super.willUpdateProps(nextProps); - Object.assign(this.data, JSON.parse(this.value)); + onWillUpdateProps(() => { + Object.assign(this.data, JSON.parse(this.value)); + }); } } ShowResequenceRenderer.template = 'account.ResequenceRenderer'; diff --git a/addons/account/static/src/js/grouped_view_widget.js b/addons/account/static/src/js/grouped_view_widget.js index 3ff897676fd..457bd897c8e 100644 --- a/addons/account/static/src/js/grouped_view_widget.js +++ b/addons/account/static/src/js/grouped_view_widget.js @@ -4,7 +4,7 @@ odoo.define('account.ShowGroupedList', function (require) { const AbstractFieldOwl = require('web.AbstractFieldOwl'); const field_registry = require('web.field_registry_owl'); -const { Component } = owl; +const { Component, onWillUpdateProps } = owl; class ListItem extends Component { } ListItem.template = 'account.GroupedItemTemplate'; @@ -17,8 +17,8 @@ ListGroup.props = ["group_vals", "options"]; class ShowGroupedList extends AbstractFieldOwl { - constructor(...args) { - super(...args); + setup() { + super.setup(); this.data = this.value ? JSON.parse(this.value) : { groups_vals: [], options: { @@ -26,10 +26,9 @@ class ShowGroupedList extends AbstractFieldOwl { columns: [], }, }; - } - async willUpdateProps(nextProps) { - await super.willUpdateProps(nextProps); - Object.assign(this.data, JSON.parse(this.value)); + onWillUpdateProps(() => { + Object.assign(this.data, JSON.parse(this.value)); + }); } } ShowGroupedList.template = 'account.GroupedListTemplate'; diff --git a/addons/account/static/src/js/tax_totals.js b/addons/account/static/src/js/tax_totals.js index 8e50c8bca65..2310c28f27d 100644 --- a/addons/account/static/src/js/tax_totals.js +++ b/addons/account/static/src/js/tax_totals.js @@ -6,29 +6,29 @@ import AbstractFieldOwl from 'web.AbstractFieldOwl'; import fieldUtils from 'web.field_utils'; import field_registry from 'web.field_registry_owl'; -const { Component, useRef, useState } = owl; +const { Component, onPatched, onWillUpdateProps, useRef, useState } = owl; /** A line of some TaxTotalsComponent, giving the values of a tax group. **/ class TaxGroupComponent extends Component { - - constructor(parent, props) { - super(parent, props); + setup() { this.inputTax = useRef('taxValueInput'); this.state = useState({value: 'readonly'}); - this.allowTaxEdition = this.__owl__.parent.mode === 'edit' ? props.allowTaxEdition : false; + this.allowTaxEdition = this.props.mode === 'edit' ? this.props.allowTaxEdition : false; + onPatched(this.onPatched); + onWillUpdateProps(this.onWillUpdateProps); } //-------------------------------------------------------------------------- // Life cycle methods //-------------------------------------------------------------------------- - willUpdateProps(nextProps) { + onWillUpdateProps(nextProps) { this.setState('readonly'); // If props are edited, we set the state to readonly } - patched() { + onPatched() { if (this.state.value === 'edit') { this.inputTax.el.focus(); // Focus the input this.inputTax.el.value = this.props.taxGroup.tax_group_amount; @@ -88,14 +88,14 @@ class TaxGroupComponent extends Component { return; } this.props.taxGroup.tax_group_amount = newValue; - this.trigger('change-tax-group', { + this.props.onChangeTaxGroup({ oldValue: this.props.taxGroup.tax_group_amount, newValue: newValue, taxGroupId: this.props.taxGroup.tax_group_id }); } } -TaxGroupComponent.props = ['taxGroup', 'allowTaxEdition', 'record']; +TaxGroupComponent.props = ['taxGroup', 'allowTaxEdition', 'record', 'onChangeTaxGroup', 'mode']; TaxGroupComponent.template = 'account.TaxGroupComponent'; /** @@ -106,13 +106,14 @@ TaxGroupComponent.template = 'account.TaxGroupComponent'; currency_id field. **/ class TaxTotalsComponent extends AbstractFieldOwl { - constructor(...args) { - super(...args); + setup() { + super.setup(); this.totals = useState({value: this.value ? JSON.parse(this.value) : null}); this.allowTaxEdition = this.nodeOptions['allowTaxEdition']; + onWillUpdateProps(this.onWillUpdateProps); } - willUpdateProps(nextProps) { + onWillUpdateProps(nextProps) { // We only reformat tax groups if there are changed this.totals.value = JSON.parse(nextProps.record.data[this.props.fieldName]); } diff --git a/addons/account/static/src/xml/tax_totals.xml b/addons/account/static/src/xml/tax_totals.xml index bbe780b546d..b46d00425b5 100644 --- a/addons/account/static/src/xml/tax_totals.xml +++ b/addons/account/static/src/xml/tax_totals.xml @@ -21,7 +21,7 @@ - + @@ -56,8 +56,9 @@ diff --git a/addons/base_automation/static/tests/base_automation_error_dialog.js b/addons/base_automation/static/tests/base_automation_error_dialog.js index 462fd92cdba..748a77d8297 100644 --- a/addons/base_automation/static/tests/base_automation_error_dialog.js +++ b/addons/base_automation/static/tests/base_automation_error_dialog.js @@ -15,10 +15,11 @@ import { RPCError } from "@web/core/network/rpc_service"; import { BaseAutomationErrorDialog } from "../src/js/base_automation_error_dialog"; import { patchWithCleanup } from "@web/../tests/helpers/utils"; import { DialogContainer } from "@web/core/dialog/dialog_container"; -import { getFixture } from "@web/../tests/helpers/utils"; +import { getFixture, mount } from "@web/../tests/helpers/utils"; import { nextTick } from "@web/../tests/helpers/utils"; -const { mount } = owl; +const { onMounted } = owl; + const serviceRegistry = registry.category("services"); QUnit.module("base_automation", {}, function () { @@ -80,7 +81,7 @@ QUnit.module("base_automation", {}, function () { const env = await makeTestEnv(); const { Component: Container, props } = registry.category("main_components").get("DialogContainer"); - const dialogContainer = await mount(Container, { target: getFixture(), env, props }); + const dialogContainer = await mount(Container, getFixture(), { env, props }); const errorEvent = new PromiseRejectionEvent("error", { reason: { message: error, @@ -107,15 +108,15 @@ QUnit.module("base_automation", {}, function () { }); patchWithCleanup(DialogContainer.prototype, { - mounted() { + setup() { this._super(); - this.el.classList.add("o_dialog_container"); + onMounted(() => this.el.classList.add("o_dialog_container")); } }); const env = await makeTestEnv(); const { Component: Container, props } = registry.category("main_components").get("DialogContainer"); - const dialogContainer = await mount(Container, { target: getFixture(), env, props }); + const dialogContainer = await mount(Container, getFixture(), { env, props }); const errorEvent = new PromiseRejectionEvent("error", { reason: { message: error, diff --git a/addons/base_import/static/src/import_records/import_records.js b/addons/base_import/static/src/import_records/import_records.js index 6fcf183eb69..4cf1909608f 100644 --- a/addons/base_import/static/src/import_records/import_records.js +++ b/addons/base_import/static/src/import_records/import_records.js @@ -1,5 +1,6 @@ /** @odoo-module **/ +import { DropdownItem } from "@web/core/dropdown/dropdown_item"; import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; @@ -32,6 +33,7 @@ export class ImportRecords extends Component { } ImportRecords.template = "base_import.ImportRecords"; +ImportRecords.components = { DropdownItem }; const importRecordsItem = { Component: ImportRecords, diff --git a/addons/base_import/static/src/import_records/import_records.xml b/addons/base_import/static/src/import_records/import_records.xml index a16a5f9f2f4..d8aa7c6a9b2 100644 --- a/addons/base_import/static/src/import_records/import_records.xml +++ b/addons/base_import/static/src/import_records/import_records.xml @@ -2,7 +2,7 @@ - + Import records diff --git a/addons/base_import/static/src/legacy/js/import_menu.js b/addons/base_import/static/src/legacy/js/import_menu.js index de557592c6f..dae3a738d79 100644 --- a/addons/base_import/static/src/legacy/js/import_menu.js +++ b/addons/base_import/static/src/legacy/js/import_menu.js @@ -1,6 +1,7 @@ odoo.define('base_import.ImportMenu', function (require) { "use strict"; + const { DropdownItem } = require('@web/core/dropdown/dropdown_item'); const FavoriteMenu = require('web.FavoriteMenu'); const { useModel } = require('web.Model'); @@ -12,8 +13,7 @@ odoo.define('base_import.ImportMenu', function (require) { * This component is used to import the records for particular model. */ class ImportMenu extends Component { - constructor() { - super(...arguments); + setup() { this.model = useModel('searchModel'); } @@ -56,6 +56,7 @@ odoo.define('base_import.ImportMenu', function (require) { ImportMenu.props = {}; ImportMenu.template = "base_import.ImportRecords"; + ImportMenu.components = { DropdownItem }; FavoriteMenu.registry.add('import-menu', ImportMenu, 1); diff --git a/addons/board/static/src/add_to_board/add_to_board.js b/addons/board/static/src/add_to_board/add_to_board.js index 3157ffd2070..d20545a0df0 100644 --- a/addons/board/static/src/add_to_board/add_to_board.js +++ b/addons/board/static/src/add_to_board/add_to_board.js @@ -1,5 +1,6 @@ /** @odoo-module **/ +import { Dropdown } from "@web/core/dropdown/dropdown"; import { registry } from "@web/core/registry"; import { useAutofocus, useService } from "@web/core/utils/hooks"; import { sprintf } from "@web/core/utils/strings"; @@ -82,6 +83,7 @@ export class AddToBoard extends Component { } AddToBoard.template = "board.AddToBoard"; +AddToBoard.components = { Dropdown }; const addToBoardItem = { Component: AddToBoard, 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 98fdb8aad9b..3f7ebe33e15 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 @@ -2,10 +2,10 @@ - + Add to my dashboard
- +
diff --git a/addons/board/static/src/legacy/js/add_to_board_menu.js b/addons/board/static/src/legacy/js/add_to_board_menu.js index 0c6deb4d18b..0059e03619a 100644 --- a/addons/board/static/src/legacy/js/add_to_board_menu.js +++ b/addons/board/static/src/legacy/js/add_to_board_menu.js @@ -3,9 +3,10 @@ odoo.define('board.AddToBoardMenu', function (require) { const Context = require('web.Context'); const Domain = require('web.Domain'); + const { Dropdown } = require('@web/core/dropdown/dropdown'); const FavoriteMenu = require('web.FavoriteMenu'); const { sprintf } = require('web.utils'); - const { useAutofocus } = require('web.custom_hooks'); + const { useAutofocus } = require("@web/core/utils/hooks"); const { Component, useState } = owl; @@ -22,9 +23,7 @@ odoo.define('board.AddToBoardMenu', function (require) { * This component is only available in actions of type 'ir.actions.act_window'. */ class AddToBoardMenu extends Component { - constructor() { - super(...arguments); - + setup() { this.interactive = true; this.state = useState({ name: this.env.action.name || "", @@ -143,6 +142,7 @@ odoo.define('board.AddToBoardMenu', function (require) { AddToBoardMenu.props = {}; AddToBoardMenu.template = 'board.AddToBoard'; + AddToBoardMenu.components = { Dropdown }; FavoriteMenu.registry.add('add-to-board-menu', AddToBoardMenu, 10); diff --git a/addons/board/static/tests/dashboard_tests.js b/addons/board/static/tests/dashboard_tests.js index 06da1a51d63..4e62f5adab7 100644 --- a/addons/board/static/tests/dashboard_tests.js +++ b/addons/board/static/tests/dashboard_tests.js @@ -8,7 +8,7 @@ odoo.define("board.dashboard_tests", function (require) { 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 { getFixture, patchWithCleanup } = require("@web/../tests/helpers/utils"); const { registry } = require("@web/core/registry"); const { makeFakeUserService } = require("@web/../tests/helpers/mock_services"); const { @@ -35,7 +35,10 @@ odoo.define("board.dashboard_tests", function (require) { const patchDate = testUtils.mock.patchDate; const favoriteMenuRegistry = registry.category("favoriteMenu"); + const { markup } = owl; + let serverData; + let target; QUnit.module("Dashboard", { beforeEach: function () { this.data = { @@ -93,6 +96,7 @@ odoo.define("board.dashboard_tests", function (require) { { sequence: 10 } ); serverData = { models: this.data }; + target = getFixture(); }, }); @@ -155,7 +159,7 @@ odoo.define("board.dashboard_tests", function (require) { "", viewOptions: { action: { - help: '

click to add a partner

', + help: markup('

click to add a partner

'), }, }, }); @@ -886,18 +890,18 @@ odoo.define("board.dashboard_tests", function (require) { views: [[false, "list"]], }); - assert.containsOnce(webClient, ".o_list_view", "should display the list view"); + assert.containsOnce(target, ".o_list_view", "should display the list view"); // Sort the list await testUtils.dom.click($(".o_column_sortable")); // Group It - await toggleGroupByMenu(webClient); - await toggleAddCustomGroup(webClient); - await applyGroup(webClient); + await toggleGroupByMenu(target); + await toggleAddCustomGroup(target); + await applyGroup(target); // add this action to dashboard - await toggleFavoriteMenu(webClient); + await toggleFavoriteMenu(target); await testUtils.dom.triggerEvent($(".o_add_to_board button.dropdown-toggle"), "mouseenter"); await testUtils.fields.editInput($(".o_add_to_board input"), "a name"); @@ -949,32 +953,32 @@ odoo.define("board.dashboard_tests", function (require) { var filter_count = 0; // Add a first filter - await toggleFilterMenu(webClient); - await toggleAddCustomFilter(webClient); - await editConditionValue(webClient, 0, "a"); - await applyFilter(webClient); + await toggleFilterMenu(target); + await toggleAddCustomFilter(target); + await editConditionValue(target, 0, "a"); + await applyFilter(target); // Add it to dashboard - await toggleFavoriteMenu(webClient); + await toggleFavoriteMenu(target); 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")); + await testUtils.dom.click(target.querySelector(".o_facet_remove")); // Add the second filter - await toggleFilterMenu(webClient); - await toggleAddCustomFilter(webClient); - await editConditionValue(webClient, 0, "b"); - await applyFilter(webClient); + await toggleFilterMenu(target); + await toggleAddCustomFilter(target); + await editConditionValue(target, 0, "b"); + await applyFilter(target); // Add it to dashboard - await toggleFavoriteMenu(webClient); + await toggleFavoriteMenu(target); await testUtils.dom.triggerEvent( - webClient.el.querySelector(".o_add_to_board button.dropdown-toggle"), + target.querySelector(".o_add_to_board button.dropdown-toggle"), "mouseenter" ); await testUtils.dom.click( - webClient.el.querySelector(".o_add_to_board .dropdown-menu button") + target.querySelector(".o_add_to_board .dropdown-menu button") ); }); @@ -1012,19 +1016,19 @@ odoo.define("board.dashboard_tests", function (require) { }); // Add a filter - await toggleFilterMenu(webClient); - await toggleAddCustomFilter(webClient); - await editConditionValue(webClient, 0, "b"); - await applyFilter(webClient); + await toggleFilterMenu(target); + await toggleAddCustomFilter(target); + await editConditionValue(target, 0, "b"); + await applyFilter(target); // Add it to dashboard - await toggleFavoriteMenu(webClient); + await toggleFavoriteMenu(target); await testUtils.dom.triggerEvent( - webClient.el.querySelector(".o_add_to_board button.dropdown-toggle"), + target.querySelector(".o_add_to_board button.dropdown-toggle"), "mouseenter" ); // add await testUtils.dom.click( - webClient.el.querySelector(".o_add_to_board .dropdown-menu button") + target.querySelector(".o_add_to_board .dropdown-menu button") ); }); @@ -1253,19 +1257,19 @@ odoo.define("board.dashboard_tests", function (require) { }); // filter on July 2020 - await toggleFilterMenu(webClient); - await toggleMenuItem(webClient, "Date"); - await toggleMenuItemOption(webClient, "Date", "July"); + await toggleFilterMenu(target); + await toggleMenuItem(target, "Date"); + await toggleMenuItemOption(target, "Date", "July"); // compare July 2020 to June 2020 - await toggleComparisonMenu(webClient); - await toggleMenuItem(webClient, 0); + await toggleComparisonMenu(target); + await toggleMenuItem(target, 0); // add the view to the dashboard - await toggleFavoriteMenu(webClient); + await toggleFavoriteMenu(target); - await mouseEnter(webClient.el.querySelector(".o_add_to_board .dropdown-toggle")); - const input = webClient.el.querySelector(".o_add_to_board .dropdown-menu input"); + await mouseEnter(target.querySelector(".o_add_to_board .dropdown-toggle")); + const input = target.querySelector(".o_add_to_board .dropdown-menu input"); await testUtils.fields.editInput(input, "Pipeline"); await testUtils.dom.click($(".o_add_to_board div button")); @@ -1301,9 +1305,9 @@ odoo.define("board.dashboard_tests", function (require) { views: [[false, "pivot"]], }); - await toggleFavoriteMenu(webClient.el); - await mouseEnter(webClient.el.querySelector(".o_add_to_board .dropdown-toggle")); - const input = webClient.el.querySelector(".o_add_to_board .dropdown-menu input"); + await toggleFavoriteMenu(target); + await mouseEnter(target.querySelector(".o_add_to_board .dropdown-toggle")); + const input = target.querySelector(".o_add_to_board .dropdown-menu input"); await testUtils.fields.editInput(input, "Pipeline"); await triggerEvent(input, null, "keydown", { key: "Enter" }); @@ -1411,9 +1415,9 @@ odoo.define("board.dashboard_tests", function (require) { context: { search_default_filter: 1 }, }); - await toggleFavoriteMenu(webClient.el); - await mouseEnter(webClient.el.querySelector(".o_add_to_board .dropdown-toggle")); - const input = webClient.el.querySelector(".o_add_to_board .dropdown-menu input"); + await toggleFavoriteMenu(target); + await mouseEnter(target.querySelector(".o_add_to_board .dropdown-toggle")); + const input = target.querySelector(".o_add_to_board .dropdown-menu input"); await testUtils.fields.editInput(input, "Pipeline"); await triggerEvent(input, null, "keydown", { key: "Enter" }); }); diff --git a/addons/bus/static/tests/assets_watchdog_tests.js b/addons/bus/static/tests/assets_watchdog_tests.js index 4a472cb2342..5c85599dbc8 100644 --- a/addons/bus/static/tests/assets_watchdog_tests.js +++ b/addons/bus/static/tests/assets_watchdog_tests.js @@ -7,25 +7,24 @@ import * as AbstractStorageService from "web.AbstractStorageService"; import { createWebClient } from "@web/../tests/webclient/helpers"; import { assetsWatchdogService } from "@bus/js/services/assets_watchdog_service"; -import { click, nextTick, patchWithCleanup } from "@web/../tests/helpers/utils"; +import { click, getFixture, nextTick, patchWithCleanup } from "@web/../tests/helpers/utils"; import { browser } from "@web/core/browser/browser"; import { registry } from "@web/core/registry"; const LocalStorageService = AbstractStorageService.extend({ storage: new RamStorage(), }); -const mainComponentRegistry = registry.category("main_components"); const serviceRegistry = registry.category("services"); QUnit.module("Bus Assets WatchDog", (hooks) => { let legacyServicesRegistry; + let target; hooks.beforeEach((assert) => { legacyServicesRegistry = new legacyRegistry(); legacyServicesRegistry.add("bus_service", BusService); legacyServicesRegistry.add("local_storage", LocalStorageService); serviceRegistry.add("assetsWatchdog", assetsWatchdogService); - patchWithCleanup(browser, { setTimeout: (fn) => fn(), clearTimeout: () => {}, @@ -33,6 +32,8 @@ QUnit.module("Bus Assets WatchDog", (hooks) => { reload: () => assert.step("reloadPage"), }, }); + + target = getFixture(); }); QUnit.test("can listen on bus and displays notifications in DOM", async (assert) => { @@ -54,21 +55,21 @@ QUnit.module("Bus Assets WatchDog", (hooks) => { } }; - const webClient = await createWebClient({ + await createWebClient({ legacyParams: { serviceRegistry: legacyServicesRegistry }, mockRPC, }); await nextTick(); - assert.containsOnce(webClient.el, ".o_notification_body"); + assert.containsOnce(target, ".o_notification_body"); assert.strictEqual( - webClient.el.querySelector(".o_notification_body .o_notification_content").textContent, + target.querySelector(".o_notification_body .o_notification_content").textContent, "The page appears to be out of date." ); // reload by clicking on the reload button - await click(webClient.el, ".o_notification_buttons .btn-primary"); + await click(target, ".o_notification_buttons .btn-primary"); assert.verifySteps(["reloadPage"]); }); }); diff --git a/addons/calendar/static/tests/calendar_notification_tests.js b/addons/calendar/static/tests/calendar_notification_tests.js index 6d23ae8268c..3992dc5bcb7 100644 --- a/addons/calendar/static/tests/calendar_notification_tests.js +++ b/addons/calendar/static/tests/calendar_notification_tests.js @@ -7,7 +7,7 @@ import * as AbstractStorageService from "web.AbstractStorageService"; import { createWebClient } from "@web/../tests/webclient/helpers"; import { calendarNotificationService } from "@calendar/js/services/calendar_notification_service"; -import { click, nextTick, patchWithCleanup } from "@web/../tests/helpers/utils"; +import { click, getFixture, nextTick, patchWithCleanup } from "@web/../tests/helpers/utils"; import { browser } from "@web/core/browser/browser"; import { registry } from "@web/core/registry"; @@ -18,7 +18,10 @@ const serviceRegistry = registry.category("services"); QUnit.module("Calendar Notification", (hooks) => { let legacyServicesRegistry; + let target; hooks.beforeEach(() => { + target = getFixture(); + legacyServicesRegistry = new legacyRegistry(); legacyServicesRegistry.add("bus_service", BusService); legacyServicesRegistry.add("local_storage", LocalStorageService); @@ -69,23 +72,23 @@ QUnit.module("Calendar Notification", (hooks) => { } }; - const webClient = await createWebClient({ + await createWebClient({ legacyParams: { serviceRegistry: legacyServicesRegistry }, mockRPC, }); await nextTick(); - assert.containsOnce(webClient.el, ".o_notification_body"); + assert.containsOnce(target, ".o_notification_body"); assert.strictEqual( - webClient.el.querySelector(".o_notification_body .o_notification_content") + target.querySelector(".o_notification_body .o_notification_content") .textContent, "Very old meeting message" ); - await click(webClient.el.querySelector(".o_notification_buttons .btn")); + await click(target.querySelector(".o_notification_buttons .btn")); assert.verifySteps(["notifyAck"]); - assert.containsNone(webClient.el, ".o_notification"); + assert.containsNone(target, ".o_notification"); } ); @@ -139,23 +142,23 @@ QUnit.module("Calendar Notification", (hooks) => { }; serviceRegistry.add("action", fakeActionService, { force: true }); - const webClient = await createWebClient({ + await createWebClient({ legacyParams: { serviceRegistry: legacyServicesRegistry }, mockRPC, }); await nextTick(); - assert.containsOnce(webClient.el, ".o_notification_body"); + assert.containsOnce(target, ".o_notification_body"); assert.strictEqual( - webClient.el.querySelector(".o_notification_body .o_notification_content") + target.querySelector(".o_notification_body .o_notification_content") .textContent, "Very old meeting message" ); - await click(webClient.el.querySelectorAll(".o_notification_buttons .btn")[1]); + await click(target.querySelectorAll(".o_notification_buttons .btn")[1]); assert.verifySteps(["calendar.action_calendar_event_notify"]); - assert.containsNone(webClient.el, ".o_notification"); + assert.containsNone(target, ".o_notification"); } ); @@ -197,23 +200,23 @@ QUnit.module("Calendar Notification", (hooks) => { } }; - const webClient = await createWebClient({ + await createWebClient({ legacyParams: { serviceRegistry: legacyServicesRegistry }, mockRPC, }); await nextTick(); - assert.containsOnce(webClient.el, ".o_notification_body"); + assert.containsOnce(target, ".o_notification_body"); assert.strictEqual( - webClient.el.querySelector(".o_notification_body .o_notification_content") + target.querySelector(".o_notification_body .o_notification_content") .textContent, "Very old meeting message" ); - await click(webClient.el.querySelectorAll(".o_notification_buttons .btn")[2]); + await click(target.querySelectorAll(".o_notification_buttons .btn")[2]); assert.verifySteps([], "should only close the notification withtout calling a rpc"); - assert.containsNone(webClient.el, ".o_notification"); + assert.containsNone(target, ".o_notification"); } ); }); diff --git a/addons/crm/static/tests/forecast_view_tests.js b/addons/crm/static/tests/forecast_view_tests.js index b1bfcb86b3f..2d078b3b50c 100644 --- a/addons/crm/static/tests/forecast_view_tests.js +++ b/addons/crm/static/tests/forecast_view_tests.js @@ -1,6 +1,6 @@ /** @odoo-module **/ -import { legacyExtraNextTick, patchWithCleanup } from "@web/../tests/helpers/utils"; +import { getFixture, legacyExtraNextTick, patchWithCleanup } from "@web/../tests/helpers/utils"; import { setupControlPanelServiceRegistry, switchView, @@ -29,6 +29,7 @@ const forecastDomain = (forecastStart) => { }; let serverData; +let target; QUnit.module("Views", (hooks) => { hooks.beforeEach(async () => { serverData = { @@ -66,6 +67,8 @@ QUnit.module("Views", (hooks) => { }; setupControlPanelServiceRegistry(); serviceRegistry.add("dialog", dialogService); + + target = getFixture(); }); QUnit.module("Forecast views"); @@ -241,24 +244,24 @@ QUnit.module("Views", (hooks) => { }, }); - assert.containsOnce(webClient, ".o_switch_view.o_graph.active"); + assert.containsOnce(target, ".o_switch_view.o_graph.active"); - await toggleGroupByMenu(webClient); - await toggleMenuItem(webClient, "Date Field"); - await toggleMenuItemOption(webClient, "Date Field", "Quarter"); + await toggleGroupByMenu(target); + await toggleMenuItem(target, "Date Field"); + await toggleMenuItemOption(target, "Date Field", "Quarter"); - await switchView(webClient, "legacy_toy"); + await switchView(target, "legacy_toy"); await legacyExtraNextTick(); - assert.containsOnce(webClient, ".o_switch_view.o_legacy_toy.active"); + assert.containsOnce(target, ".o_switch_view.o_legacy_toy.active"); - await cpHelpers.toggleGroupByMenu(webClient); - await toggleMenuItem(webClient, "Date Field"); - await toggleMenuItemOption(webClient, "Date Field", "Year"); + await cpHelpers.toggleGroupByMenu(target); + await toggleMenuItem(target, "Date Field"); + await toggleMenuItemOption(target, "Date Field", "Year"); - await switchView(webClient, "graph"); + await switchView(target, "graph"); - assert.containsOnce(webClient, ".o_switch_view.o_graph.active"); + assert.containsOnce(target, ".o_switch_view.o_graph.active"); // second doAction legacy_toy -> forecast_graph -> legacy_toy @@ -277,24 +280,24 @@ QUnit.module("Views", (hooks) => { }); await legacyExtraNextTick(); - assert.containsOnce(webClient, ".o_switch_view.o_legacy_toy.active"); + assert.containsOnce(target, ".o_switch_view.o_legacy_toy.active"); - await cpHelpers.toggleGroupByMenu(webClient); - await toggleMenuItem(webClient, "Date Field"); - await toggleMenuItemOption(webClient, "Date Field", "Quarter"); + await cpHelpers.toggleGroupByMenu(target); + await toggleMenuItem(target, "Date Field"); + await toggleMenuItemOption(target, "Date Field", "Quarter"); - await switchView(webClient, "graph"); + await switchView(target, "graph"); - assert.containsOnce(webClient, ".o_switch_view.o_graph.active"); + assert.containsOnce(target, ".o_switch_view.o_graph.active"); - await toggleGroupByMenu(webClient); - await toggleMenuItem(webClient, "Date Field"); - await toggleMenuItemOption(webClient, "Date Field", "Year"); + await toggleGroupByMenu(target); + await toggleMenuItem(target, "Date Field"); + await toggleMenuItemOption(target, "Date Field", "Year"); - await switchView(webClient, "legacy_toy"); + await switchView(target, "legacy_toy"); await legacyExtraNextTick(); - assert.containsOnce(webClient, ".o_switch_view.o_legacy_toy.active"); + assert.containsOnce(target, ".o_switch_view.o_legacy_toy.active"); unpatchDate(); } diff --git a/addons/event_sale/static/tests/event_configurator.test.js b/addons/event_sale/static/tests/event_configurator.test.js index 59920a54b82..7a462379bb7 100644 --- a/addons/event_sale/static/tests/event_configurator.test.js +++ b/addons/event_sale/static/tests/event_configurator.test.js @@ -77,6 +77,9 @@ odoo.define('event.configurator.tests', function (require) { } }, function (){ QUnit.test('Select a regular product and verify that the event configurator is not opened', async function (assert) { + // When the module event_booth_sale is installed, it will also call the rpc: read/detailed_type when selecting a product. + // This means that 2 asserts are done when this module is installed. + // Note that: this test will fail if the module event_booth_sale is not installed. assert.expect(2); var form = await createView({ diff --git a/addons/google_drive/static/src/xml/gdrive.xml b/addons/google_drive/static/src/xml/gdrive.xml index db1e3a11c8a..c7765d78a0f 100644 --- a/addons/google_drive/static/src/xml/gdrive.xml +++ b/addons/google_drive/static/src/xml/gdrive.xml @@ -6,7 +6,7 @@ role="menuitem" class="o_menu_item oe_share_gdoc_item dropdown-item" > - + diff --git a/addons/google_spreadsheet/static/src/add_to_google_spreadsheet/add_to_google_spreadsheet.js b/addons/google_spreadsheet/static/src/add_to_google_spreadsheet/add_to_google_spreadsheet.js index 970b6e83ff1..589c127f58d 100644 --- a/addons/google_spreadsheet/static/src/add_to_google_spreadsheet/add_to_google_spreadsheet.js +++ b/addons/google_spreadsheet/static/src/add_to_google_spreadsheet/add_to_google_spreadsheet.js @@ -1,5 +1,6 @@ /** @odoo-module **/ +import { DropdownItem } from "@web/core/dropdown/dropdown_item"; import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; @@ -41,6 +42,7 @@ export class AddToGoogleSpreadsheet extends Component { } AddToGoogleSpreadsheet.template = "google_spreadsheet.AddToGoogleSpreadsheet"; +AddToGoogleSpreadsheet.components = { DropdownItem }; const addToGoogleSpreadsheetItem = { Component: AddToGoogleSpreadsheet, diff --git a/addons/google_spreadsheet/static/src/add_to_google_spreadsheet/add_to_google_spreadsheet.xml b/addons/google_spreadsheet/static/src/add_to_google_spreadsheet/add_to_google_spreadsheet.xml index b5ce85aa399..b32a7a4621f 100644 --- a/addons/google_spreadsheet/static/src/add_to_google_spreadsheet/add_to_google_spreadsheet.xml +++ b/addons/google_spreadsheet/static/src/add_to_google_spreadsheet/add_to_google_spreadsheet.xml @@ -2,7 +2,7 @@ - + Add to Google Spreadsheet diff --git a/addons/google_spreadsheet/static/src/legacy/js/add_to_google_spreadsheet_menu.js b/addons/google_spreadsheet/static/src/legacy/js/add_to_google_spreadsheet_menu.js index 5cd1d75c634..2b74fda6d10 100644 --- a/addons/google_spreadsheet/static/src/legacy/js/add_to_google_spreadsheet_menu.js +++ b/addons/google_spreadsheet/static/src/legacy/js/add_to_google_spreadsheet_menu.js @@ -2,6 +2,7 @@ odoo.define('board.AddToGoogleSpreadsheetMenu', function (require) { "use strict"; const Domain = require('web.Domain'); + const { DropdownItem } = require('@web/core/dropdown/dropdown_item'); const FavoriteMenu = require('web.FavoriteMenu'); const { Component } = owl; @@ -56,6 +57,7 @@ odoo.define('board.AddToGoogleSpreadsheetMenu', function (require) { AddToGoogleSpreadsheetMenu.props = {}; AddToGoogleSpreadsheetMenu.template = "google_spreadsheet.AddToGoogleSpreadsheet"; + AddToGoogleSpreadsheetMenu.components = { DropdownItem }; FavoriteMenu.registry.add('add-to-google-spreadsheet-menu', AddToGoogleSpreadsheetMenu, 20); diff --git a/addons/hr_holidays/static/src/components/thread_view/tests/thread_view_tests.js b/addons/hr_holidays/static/src/components/thread_view/tests/thread_view_tests.js index b8cc907c2ca..809fe732a7a 100644 --- a/addons/hr_holidays/static/src/components/thread_view/tests/thread_view_tests.js +++ b/addons/hr_holidays/static/src/components/thread_view/tests/thread_view_tests.js @@ -16,9 +16,9 @@ QUnit.module('thread_view_tests.js', { this.start = async params => { const res = await start({ ...params, data: this.data }); - const { afterEvent, components, env, widget } = res; + const { afterEvent, apps, env, widget } = res; this.afterEvent = afterEvent; - this.components = components; + this.apps = apps; this.env = env; this.widget = widget; return res; diff --git a/addons/hr_holidays/static/src/dashboard/time_off_card.js b/addons/hr_holidays/static/src/dashboard/time_off_card.js index 6443516b5ef..70440fcd00e 100644 --- a/addons/hr_holidays/static/src/dashboard/time_off_card.js +++ b/addons/hr_holidays/static/src/dashboard/time_off_card.js @@ -1,8 +1,11 @@ /* @odoo-module */ +import Popover from "web.Popover"; + const { Component } = owl; export class TimeOffCardPopover extends Component {} +TimeOffCardPopover.components = { Popover }; TimeOffCardPopover.template = 'hr_holidays.TimeOffCardPopover'; TimeOffCardPopover.props = ['allocated', 'approved', 'planned', 'left']; diff --git a/addons/hr_holidays/static/src/leave_stats/leave_stats.js b/addons/hr_holidays/static/src/leave_stats/leave_stats.js index c67944daecf..534c2cdafc5 100644 --- a/addons/hr_holidays/static/src/leave_stats/leave_stats.js +++ b/addons/hr_holidays/static/src/leave_stats/leave_stats.js @@ -4,37 +4,35 @@ import { useService } from '@web/core/utils/hooks'; import { getLangDateFormat } from 'web.time'; import widgetRegistry from 'web.widgetRegistry'; -const { Component, useState } = owl; +const { Component, useState, onWillStart, onWillUpdateProps } = owl; export class LeaveStatsComponent extends Component { setup() { - super.setup(...arguments); this.rpc = useService('rpc'); this.state = useState({ leaves: [], departmentLeaves: [], }); - } - async willStart() { - await this.loadLeaves(this.date, this.employee); - await this.loadDepartmentLeaves(this.date, this.department, this.employee); - } + onWillStart(async () => { + await this.loadLeaves(this.date, this.employee); + await this.loadDepartmentLeaves(this.date, this.department, this.employee); + }); + onWillUpdateProps(async (nextProps) => { + const dateFrom = nextProps.record.data.date_from || moment(); + const dateChanged = this.date !== dateFrom; + const employee = nextProps.record.data.employee_id.data; + const department = nextProps.record.data.department_id.data; - async willUpdateProps(nextProps) { - const dateFrom = nextProps.record.data.date_from || moment(); - const dateChanged = this.date !== dateFrom; - const employee = nextProps.record.data.employee_id.data; - const department = nextProps.record.data.department_id.data; + if (dateChanged || employee && (this.employee && this.employee.id) !== employee.id) { + await this.loadLeaves(dateFrom, employee); + } - if (dateChanged || employee && (this.employee && this.employee.id) !== employee.id) { - await this.loadLeaves(dateFrom, employee); - } - - if (dateChanged || department && (this.department && this.department.id) !== department.id) { - await this.loadDepartmentLeaves(dateFrom, department, employee); - } + if (dateChanged || department && (this.department && this.department.id) !== department.id) { + await this.loadDepartmentLeaves(dateFrom, department, employee); + } + }) } get date() { diff --git a/addons/hr_holidays/static/src/leave_stats/leave_stats.xml b/addons/hr_holidays/static/src/leave_stats/leave_stats.xml index d0a7e65c400..8611bec818b 100644 --- a/addons/hr_holidays/static/src/leave_stats/leave_stats.xml +++ b/addons/hr_holidays/static/src/leave_stats/leave_stats.xml @@ -8,7 +8,7 @@
None
-
+
day(s)
@@ -21,7 +21,7 @@
None
-
+
: day(s) -
diff --git a/addons/hr_recruitment/static/src/js/tours/hr_recruitment.js b/addons/hr_recruitment/static/src/js/tours/hr_recruitment.js index 0b2874f5fd4..a0819049f11 100644 --- a/addons/hr_recruitment/static/src/js/tours/hr_recruitment.js +++ b/addons/hr_recruitment/static/src/js/tours/hr_recruitment.js @@ -5,9 +5,11 @@ const {_t} = require('web.core'); const {Markup} = require('web.utils'); var tour = require('web_tour.tour'); +const { markup } = owl; + tour.register('hr_recruitment_tour',{ url: "/web", - rainbowManMessage: _t("
Great job! You hired a new colleague!
Try the Website app to publish job offers online.
"), + rainbowManMessage: markup(_t("
Great job! You hired a new colleague!
Try the Website app to publish job offers online.
")), fadeout: 'very_slow', sequence: 230, }, [tour.stepUtils.showAppsMenuItem(), { diff --git a/addons/iap/static/src/js/insufficient_credit_error_handler.js b/addons/iap/static/src/js/insufficient_credit_error_handler.js index 24c76affbf6..3271daa7b54 100644 --- a/addons/iap/static/src/js/insufficient_credit_error_handler.js +++ b/addons/iap/static/src/js/insufficient_credit_error_handler.js @@ -7,9 +7,10 @@ class InsufficientCreditDialog extends Dialog { setup() { super.setup(...arguments); this.orm = useService("orm"); + owl.onWillStart(this.onWillStart); } - async willStart() { + async onWillStart() { const { errorData } = this.props; this.url = await this.orm.call("iap.account", "get_credits_url", [], { base_url: errorData.base_url, diff --git a/addons/lunch/static/src/xml/lunch_templates.xml b/addons/lunch/static/src/xml/lunch_templates.xml index 035f5c2da56..264d21b0537 100644 --- a/addons/lunch/static/src/xml/lunch_templates.xml +++ b/addons/lunch/static/src/xml/lunch_templates.xml @@ -1,7 +1,7 @@ - + @@ -43,7 +43,7 @@
    -
  • +
  • -
    +
    +
    diff --git a/addons/product/static/tests/product_pricelist_report_test.js b/addons/product/static/tests/product_pricelist_report_test.js index 7e48a976fb0..9774d3cad15 100644 --- a/addons/product/static/tests/product_pricelist_report_test.js +++ b/addons/product/static/tests/product_pricelist_report_test.js @@ -4,6 +4,7 @@ const GeneratePriceList = require('product.generate_pricelist').GeneratePriceLis const testUtils = require('web.test_utils'); const { createWebClient, doAction } = require('@web/../tests/webclient/helpers'); +const { getFixture, patchWithCleanup } = require("@web/../tests/helpers/utils"); let serverData; @@ -38,9 +39,8 @@ QUnit.module('Product Pricelist', { QUnit.test('Pricelist Client Action', async function (assert) { assert.expect(21); - const self = this; let Qty = [1, 5, 10]; // default quantities - testUtils.mock.patch(GeneratePriceList, { + patchWithCleanup(GeneratePriceList.prototype, { _onFieldChanged: function (event) { assert.step('field_changed'); return this._super.apply(this, arguments); @@ -57,6 +57,7 @@ QUnit.module('Product Pricelist', { } }; + const target = getFixture(); const webClient = await createWebClient({ serverData, mockRPC }); await doAction(webClient, { id: 1, @@ -72,7 +73,7 @@ QUnit.module('Product Pricelist', { }); // checking default pricelist - assert.strictEqual($(webClient.el).find('.o_field_many2one input').val(), "Public Pricelist", + assert.strictEqual($(target).find('.o_field_many2one input').val(), "Public Pricelist", "should have default pricelist"); // changing pricelist @@ -80,28 +81,28 @@ QUnit.module('Product Pricelist', { await testUtils.fields.many2one.clickItem("pricelist_id", "Test"); // check wherther pricelist value has been updated or not. along with that check default quantities should be there. - assert.strictEqual($(webClient.el).find('.o_field_many2one input').val(), "Test", + assert.strictEqual($(target).find('.o_field_many2one input').val(), "Test", "After pricelist change, the pricelist_id field should be updated"); - assert.strictEqual($(webClient.el).find('.o_badges > .badge').length, 3, + assert.strictEqual($(target).find('.o_badges > .badge').length, 3, "There should be 3 default Quantities"); // existing quantity can not be added. - await testUtils.dom.click($(webClient.el).find('.o_add_qty')); + await testUtils.dom.click($(target).find('.o_add_qty')); let notificationElement = document.body.querySelector('.o_notification_manager .o_notification.bg-info'); assert.strictEqual(notificationElement.querySelector('.o_notification_content').textContent, "Quantity already present (1).", "Existing Quantity can not be added"); // adding few more quantities to check. - $(webClient.el).find('.o_product_qty').val(2); + $(target).find('.o_product_qty').val(2); Qty.push(2); - await testUtils.dom.click($(webClient.el).find('.o_add_qty')); - $(webClient.el).find('.o_product_qty').val(3); + await testUtils.dom.click($(target).find('.o_add_qty')); + $(target).find('.o_product_qty').val(3); Qty.push(3); - await testUtils.dom.click($(webClient.el).find('.o_add_qty')); + await testUtils.dom.click($(target).find('.o_add_qty')); // should not be added more then 5 quantities. - $(webClient.el).find('.o_product_qty').val(4); - await testUtils.dom.click($(webClient.el).find('.o_add_qty')); + $(target).find('.o_product_qty').val(4); + await testUtils.dom.click($(target).find('.o_add_qty')); notificationElement = document.body.querySelector('.o_notification_manager .o_notification.bg-warning'); assert.strictEqual(notificationElement.querySelector('.o_notification_content').textContent, @@ -110,15 +111,15 @@ QUnit.module('Product Pricelist', { // removing all the quantities should work Qty.pop(10); - await testUtils.dom.click($(webClient.el).find('.o_badges .badge:contains("10") .o_remove_qty')); + await testUtils.dom.click($(target).find('.o_badges .badge:contains("10") .o_remove_qty')); Qty.pop(5); - await testUtils.dom.click($(webClient.el).find('.o_badges .badge:contains("5") .o_remove_qty')); + await testUtils.dom.click($(target).find('.o_badges .badge:contains("5") .o_remove_qty')); Qty.pop(3); - await testUtils.dom.click($(webClient.el).find('.o_badges .badge:contains("3") .o_remove_qty')); + await testUtils.dom.click($(target).find('.o_badges .badge:contains("3") .o_remove_qty')); Qty.pop(2); - await testUtils.dom.click($(webClient.el).find('.o_badges .badge:contains("2") .o_remove_qty')); + await testUtils.dom.click($(target).find('.o_badges .badge:contains("2") .o_remove_qty')); Qty.pop(1); - await testUtils.dom.click($(webClient.el).find('.o_badges .badge:contains("1") .o_remove_qty')); + await testUtils.dom.click($(target).find('.o_badges .badge:contains("1") .o_remove_qty')); assert.verifySteps([ 'field_changed', @@ -130,8 +131,6 @@ QUnit.module('Product Pricelist', { 'qty_changed', 'qty_changed' ]); - - testUtils.mock.unpatch(GeneratePriceList); }); } diff --git a/addons/project/static/src/js/project_control_panel.js b/addons/project/static/src/js/project_control_panel.js index 7107bae4dab..fe94336b977 100644 --- a/addons/project/static/src/js/project_control_panel.js +++ b/addons/project/static/src/js/project_control_panel.js @@ -3,26 +3,17 @@ import ControlPanel from 'web.ControlPanel'; import session from 'web.session'; +const { onWillStart, onWillUpdateProps } = owl; + export class ProjectControlPanel extends ControlPanel { - constructor() { - super(...arguments); - this.show_project_update = this.env.view.type === "form" || this.props.action.context.show_project_update; + setup() { + super.setup(); + this.show_project_update = this.props.view.type === "form" || this.props.action.context.show_project_update; this.project_id = this.show_project_update ? this.props.action.context.active_id : false; - } - async willStart() { - const promises = []; - promises.push(super.willStart(...arguments)); - promises.push(this._loadWidgetData()); - return Promise.all(promises); - } - - async willUpdateProps() { - const promises = []; - promises.push(super.willUpdateProps(...arguments)); - promises.push(this._loadWidgetData()); - return Promise.all(promises); + onWillStart(() => this._loadWidgetData()); + onWillUpdateProps(() => this._loadWidgetData()); } async _loadWidgetData() { diff --git a/addons/project/static/src/js/right_panel/project_right_panel.js b/addons/project/static/src/js/right_panel/project_right_panel.js index 3c4e3c4c48e..f5c5158b62f 100644 --- a/addons/project/static/src/js/right_panel/project_right_panel.js +++ b/addons/project/static/src/js/right_panel/project_right_panel.js @@ -3,11 +3,10 @@ import { AddMilestone, OpenMilestone } from '@project/js/right_panel/project_utils'; import { formatFloat } from "@web/fields/formatters"; -const { Component, useState } = owl; +const { Component, onWillStart, onWillUpdateProps, useState } = owl; export default class ProjectRightPanel extends Component { - constructor() { - super(...arguments); + setup() { this.context = this.props.action.context; this.domain = this.props.action.domain; this.project_id = this.context.active_id; @@ -19,22 +18,15 @@ export default class ProjectRightPanel extends Component { user: {}, } }); + + onWillStart(() => this._loadQwebContext()); + onWillUpdateProps(() => this._loadQwebContext()); } formatFloat(value) { return formatFloat(value, { digits: [false, 1] }); } - async willStart() { - await super.willStart(...arguments); - await this._loadQwebContext(); - } - - async willUpdateProps() { - await super.willUpdateProps(...arguments); - await this._loadQwebContext(); - } - async _loadQwebContext() { const data = await this.rpc({ model: 'project.project', diff --git a/addons/project/static/src/js/right_panel/project_utils.js b/addons/project/static/src/js/right_panel/project_utils.js index bb249f6d39b..99bbdc6c3d1 100644 --- a/addons/project/static/src/js/right_panel/project_utils.js +++ b/addons/project/static/src/js/right_panel/project_utils.js @@ -2,25 +2,17 @@ import { _lt } from 'web.core'; import fieldUtils from 'web.field_utils'; -import { ComponentAdapter } from 'web.OwlCompatibility'; +import { ComponentAdapter, standaloneAdapter } from 'web.OwlCompatibility'; import { FormViewDialog } from 'web.view_dialogs'; -const { Component, useRef, useState } = owl; +const { Component, onWillUpdateProps, useRef, useState } = owl; class MilestoneComponent extends Component { - constructor() { - super(...arguments); + setup() { + super.setup(); this.contextValue = Object.assign({}, { 'default_project_id': this.props.context.active_id, }, this.props.context); - this.FormViewDialog = FormViewDialog; - this.state = useState({ - openDialog: false - }); - this._dialogRef = useRef('dialog'); - this._isDialogOpen = false; - this._createContext = this._createContext.bind(this); - this._onDialogSaved = this._onDialogSaved.bind(this); } get context() { @@ -33,65 +25,55 @@ class MilestoneComponent extends Component { }, value); } - dialogClosed() { - this._isDialogOpen = false; - this.state.openDialog = false; - } - - patched() { - if (this.state.openDialog && !this._isDialogOpen) { - this._isDialogOpen = true; - this._dialogRef.comp.widget.on('closed', this, () => { - this.dialogClosed(); - }); - this._dialogRef.comp.widget.open(); - } - } - - _createContext() { - return Object.assign({}, { - 'default_project_id': this.contextValue.active_id, - }, this.contextValue); - } - - async _onDialogSaved() { - await this.__owl__.parent.willUpdateProps(); + async openLegacyFormDialog(params) { + const adapterParent = standaloneAdapter({ Component }); + const dialog = new FormViewDialog(adapterParent, { + context: this.context, + res_model: "project.milestone", + res_id: false, + on_saved: this.props.onMilestoneUpdate, + ...params, + }); + await dialog.open(); + return dialog; } } MilestoneComponent.components = { ComponentAdapter }; export class AddMilestone extends MilestoneComponent { - get NEW_PROJECT_MILESTONE() { - return _lt("New Milestone"); - } - onAddMilestoneClick(event) { - if (!this._isDialogOpen) { - event.stopPropagation(); - this.state.openDialog = true; - } + event.stopPropagation(); + this.openLegacyFormDialog({ + _createContext: () => { + return { + default_project_id: this.contextValue.active_id, + ...this.contextValue + }; + }, + title: this.env._t("New Milestone"), + multi_select: true + }); } } AddMilestone.template = 'project.AddMilestone'; export class OpenMilestone extends MilestoneComponent { - constructor() { - super(...arguments); + setup() { + super.setup(); this.milestone = useState(this.props.milestone); - this.state.colorClass = this.milestone.is_deadline_exceeded ? "o_milestone_danger" : ""; - this.state.checkboxIcon = this.milestone.is_reached ? "fa-check-square-o" : "fa-square-o"; - } - - get OPEN_PROJECT_MILESTONE() { - return _lt("Milestone"); + this.state = useState({ + colorClass: this.milestone.is_deadline_exceeded ? "o_milestone_danger" : "", + checkboxIcon: this.milestone.is_reached ? "fa-check-square-o" : "fa-square-o", + }); + onWillUpdateProps(this.onWillUpdateProps); } get deadline() { return fieldUtils.format.date(moment(this.milestone.deadline)); } - willUpdateProps(nextProps) { + onWillUpdateProps(nextProps) { if (nextProps.milestone) { this.milestone = nextProps.milestone; this.state.colorClass = this.milestone.is_deadline_exceeded ? "o_milestone_danger" : ""; @@ -102,24 +84,27 @@ export class OpenMilestone extends MilestoneComponent { } } - dialogClosed() { - super.dialogClosed(); - this.write_mutex = false; - } - async onDeleteMilestone() { await this.rpc({ model: 'project.milestone', method: 'unlink', args: [this.milestone.id] }); - await this.__owl__.parent.willUpdateProps(); + await this.props.onMilestoneUpdate(); } - onOpenMilestone() { - if (!this._isDialogOpen && !this.write_mutex) { + async onOpenMilestone() { + if (!this.write_mutex) { this.write_mutex = true; - this.state.openDialog = true; + const dialog = await this.openLegacyFormDialog({ + res_id: this.milestone.id, + title: this.env._t("Milestone"), + disable_multiple_selection: true, + deletable: true + }); + dialog.on('closed', this, () => { + this.write_mutex = false; + }); } } diff --git a/addons/project/static/src/js/tours/project.js b/addons/project/static/src/js/tours/project.js index c90d2eab0bb..c9eb6cc0465 100644 --- a/addons/project/static/src/js/tours/project.js +++ b/addons/project/static/src/js/tours/project.js @@ -8,7 +8,7 @@ var tour = require('web_tour.tour'); tour.register('project_tour', { sequence: 110, url: "/web", - rainbowManMessage: "Congratulations, you are now a master of project management.", + rainbowManMessage: _t("Congratulations, you are now a master of project management."), }, [tour.stepUtils.showAppsMenuItem(), { trigger: '.o_app[data-menu-xmlid="project.menu_main_pm"]', content: Markup(_t('Want a better way to manage your projects? It starts here.')), diff --git a/addons/project/static/src/project_control_panel/project_control_panel.js b/addons/project/static/src/project_control_panel/project_control_panel.js index 29e12668102..c1be646267f 100644 --- a/addons/project/static/src/project_control_panel/project_control_panel.js +++ b/addons/project/static/src/project_control_panel/project_control_panel.js @@ -3,30 +3,28 @@ import { ControlPanel } from "@web/search/control_panel/control_panel"; import { useService } from "@web/core/utils/hooks"; +const { onWillStart, onWillUpdateProps } = owl; + export class ProjectControlPanel extends ControlPanel { - constructor() { - super(...arguments); + setup() { + super.setup(); this.orm = useService("orm"); this.user = useService("user"); const { active_id, show_project_update } = this.env.searchModel.globalContext; this.showProjectUpdate = this.env.config.viewType === "form" || show_project_update; this.projectId = this.showProjectUpdate ? active_id : false; - } - async willStart() { - const proms = [super.willStart(...arguments)]; - if (this.showProjectUpdate) { - proms.push(this.loadData()); - } - await Promise.all(proms); - } + onWillStart(async () => { + if (this.showProjectUpdate) { + await this.loadData(); + } + }); - async willUpdateProps() { - const proms = [super.willUpdateProps(...arguments)]; - if (this.showProjectUpdate) { - proms.push(this.loadData()); - } - await Promise.all(proms); + onWillUpdateProps(async () => { + if (this.showProjectUpdate) { + await this.loadData(); + } + }); } async loadData() { diff --git a/addons/project/static/src/project_sharing/project_sharing.js b/addons/project/static/src/project_sharing/project_sharing.js index 380ac46a5b9..0f9a53db202 100644 --- a/addons/project/static/src/project_sharing/project_sharing.js +++ b/addons/project/static/src/project_sharing/project_sharing.js @@ -1,13 +1,13 @@ /** @odoo-module **/ -import { useBus, useEffect, useService } from '@web/core/utils/hooks'; +import { useBus, useService } from '@web/core/utils/hooks'; import { ActionContainer } from '@web/webclient/actions/action_container'; import { MainComponentsContainer } from "@web/core/main_components_container"; import { useOwnDebugContext } from "@web/core/debug/debug_context"; import { ErrorHandler, NotUpdatable } from "@web/core/utils/components"; import { session } from '@web/session'; -const { Component } = owl; +const { Component, useEffect } = owl; export class ProjectSharingWebClient extends Component { setup() { @@ -29,8 +29,6 @@ export class ProjectSharingWebClient extends Component { ); } - mounted() { } - handleComponentError(error, C) { // remove the faulty component this.Components.splice(this.Components.indexOf(C), 1); diff --git a/addons/project/static/src/xml/project_templates.xml b/addons/project/static/src/xml/project_templates.xml index 936ac518c27..6f3c6426fe0 100644 --- a/addons/project/static/src/xml/project_templates.xml +++ b/addons/project/static/src/xml/project_templates.xml @@ -4,7 +4,7 @@ - @@ -37,7 +37,7 @@
    - +
    - +
    Track major progress points that must be reached to achieve success. @@ -84,21 +84,6 @@ @@ -118,21 +103,6 @@ - - -
    diff --git a/addons/sale/static/src/js/tours/sale.js b/addons/sale/static/src/js/tours/sale.js index 6dd8c36c231..5b7470e9102 100644 --- a/addons/sale/static/src/js/tours/sale.js +++ b/addons/sale/static/src/js/tours/sale.js @@ -5,6 +5,8 @@ const {_t} = require('web.core'); const {Markup} = require('web.utils'); var tour = require('web_tour.tour'); +const { markup } = owl; + tour.register("sale_tour", { url: "/web", rainbowMan: false, @@ -58,7 +60,7 @@ tour.register("sale_tour", { tour.register("sale_quote_tour", { url: "/web#action=sale.action_quotations_with_onboarding&view_type=form", rainbowMan: true, - rainbowManMessage: "Congratulations, your first quotation is sent!
    Check your email to validate the quote.", + rainbowManMessage: markup(_t("Congratulations, your first quotation is sent!
    Check your email to validate the quote.")), sequence: 30, }, [{ trigger: ".o_form_editable .o_field_many2one[name='partner_id']", diff --git a/addons/stock/static/tests/stock_traceability_report_backend_tests.js b/addons/stock/static/tests/stock_traceability_report_backend_tests.js index bdd7fc8375b..3c7c87b006c 100644 --- a/addons/stock/static/tests/stock_traceability_report_backend_tests.js +++ b/addons/stock/static/tests/stock_traceability_report_backend_tests.js @@ -5,12 +5,18 @@ odoo.define('stock.stock_traceability_report_backend_tests', function (require) const dom = require('web.dom'); const StockReportGeneric = require('stock.stock_report_generic'); const testUtils = require('web.test_utils'); - const { patch, unpatch } = require('web.utils'); const { dom: domUtils } = testUtils; - const { legacyExtraNextTick } = require("@web/../tests/helpers/utils"); + const { + destroy, + getFixture, + legacyExtraNextTick, + patchWithCleanup, + } = require("@web/../tests/helpers/utils"); const { createWebClient, doAction } = require('@web/../tests/webclient/helpers'); + const { onMounted, onWillUnmount } = owl; + /** * Helper function to instantiate a stock report action. * @param {Object} params @@ -80,17 +86,17 @@ odoo.define('stock.stock_traceability_report_backend_tests', function (require) let mountCount = 0; - patch(ControlPanel.prototype, 'test.ControlPanel', { - mounted() { - mountCount = mountCount + 1; - this.__uniqueId = mountCount; - assert.step(`mounted ${this.__uniqueId}`); - this.__superMounted = this._super.bind(this); - this.__superMounted(...arguments); - }, - willUnmount() { - assert.step(`willUnmount ${this.__uniqueId}`); - this.__superMounted(...arguments); + patchWithCleanup(ControlPanel.prototype, { + setup() { + this._super(); + onMounted(() => { + mountCount = mountCount + 1; + this.__uniqueId = mountCount; + assert.step(`mounted ${this.__uniqueId}`); + }); + onWillUnmount(() => { + assert.step(`willUnmount ${this.__uniqueId}`); + }); }, }); const serverData = { @@ -120,6 +126,7 @@ odoo.define('stock.stock_traceability_report_backend_tests', function (require) }, }; + const target = getFixture(); const webClient = await createWebClient({ serverData, mockRPC: function (route) { @@ -132,11 +139,12 @@ odoo.define('stock.stock_traceability_report_backend_tests', function (require) }); await doAction(webClient, 42); - await domUtils.click($(webClient.el).find('.o_stock_reports_web_action')); + await domUtils.click(target.querySelector('.o_stock_reports_web_action')); await legacyExtraNextTick(); - await domUtils.click($(webClient.el).find('.breadcrumb-item:first')); + await domUtils.click(target.querySelector('.breadcrumb-item')); await legacyExtraNextTick(); - webClient.destroy(); + + destroy(webClient); assert.verifySteps([ 'mounted 1', @@ -146,8 +154,6 @@ odoo.define('stock.stock_traceability_report_backend_tests', function (require) 'mounted 3', 'willUnmount 3', ]); - - unpatch(ControlPanel.prototype, 'test.ControlPanel'); }); }); }); diff --git a/addons/web_tour/static/src/debug/tour_dialog_component.xml b/addons/web_tour/static/src/debug/tour_dialog_component.xml index 03a3b2d9913..d299e315633 100644 --- a/addons/web_tour/static/src/debug/tour_dialog_component.xml +++ b/addons/web_tour/static/src/debug/tour_dialog_component.xml @@ -30,7 +30,7 @@
    - + diff --git a/addons/web_tour/static/src/js/tour_manager.js b/addons/web_tour/static/src/js/tour_manager.js index c5d3f098051..a7ebe558208 100644 --- a/addons/web_tour/static/src/js/tour_manager.js +++ b/addons/web_tour/static/src/js/tour_manager.js @@ -12,8 +12,10 @@ var ServicesMixin = require('web.ServicesMixin'); var session = require('web.session'); var Tip = require('web_tour.Tip'); const {Markup} = require('web.utils'); +const { config: transitionConfig } = require("@web/core/transition"); var _t = core._t; +const { markup } = owl; var RUNNING_TOUR_TIMEOUT = 10000; @@ -39,6 +41,12 @@ return core.Class.extend(mixins.EventDispatcherMixin, ServicesMixin, { }); this.disabled = disabled; this.running_tour = local_storage.getItem(get_running_key()); + if (this.running_tour) { + // Transitions can cause DOM mutations which will cause the tour_service + // MutationObserver to wait longer before proceeding to the next step + // this can slow down tours + transitionConfig.disabled = true; + } this.running_step_delay = parseInt(local_storage.getItem(get_running_delay_key()), 10) || 0; this.edition = (_.last(session.server_version_info) === 'e') ? 'enterprise' : 'community'; this._log = []; @@ -430,14 +438,13 @@ return core.Class.extend(mixins.EventDispatcherMixin, ServicesMixin, { if (message) { message = typeof message === 'function' ? message() : message; } else { - message = _t('Good job! You went through all steps of this tour.'); + message = markup(_t('Good job! You went through all steps of this tour.')); } const fadeout = this.tours[tour_name].fadeout; core.bus.trigger('show-effect', { type: "rainbow_man", message, fadeout, - messageIsHtml: true, }); } this.tours[tour_name].current_step = 0; diff --git a/addons/web_tour/static/tests/debug_manager_tests.js b/addons/web_tour/static/tests/debug_manager_tests.js index f7972737fe4..9a8daa0bf7c 100644 --- a/addons/web_tour/static/tests/debug_manager_tests.js +++ b/addons/web_tour/static/tests/debug_manager_tests.js @@ -7,15 +7,12 @@ import { ormService } from "@web/core/orm_service"; import { registry } from "@web/core/registry"; import { uiService } from "@web/core/ui/ui_service"; -import { click, getFixture } from "@web/../tests/helpers/utils"; -import { registerCleanup } from "@web/../tests/helpers/cleanup"; +import { click, getFixture, mount } from "@web/../tests/helpers/utils"; import { makeTestEnv } from "@web/../tests/helpers/mock_env"; import { makeFakeLocalizationService } from "@web/../tests/helpers/mock_services"; import { DebugMenuParent } from "@web/../tests/core/debug/debug_manager_tests"; import { fakeCommandService } from "@web/../tests/helpers/mock_services"; -const { mount } = owl; - const debugRegistry = registry.category("debug"); let target; @@ -60,8 +57,7 @@ QUnit.module("Tours", (hooks) => { }; const env = await makeTestEnv({ mockRPC }); - const debugManager = await mount(DebugMenuParent, { env, target }); - registerCleanup(() => debugManager.destroy()); + const debugManager = await mount(DebugMenuParent, target, { env }); await click(debugManager.el.querySelector("button.dropdown-toggle")); diff --git a/addons/web_tour/static/tests/tour_manager_tests.js b/addons/web_tour/static/tests/tour_manager_tests.js index 3d2a4bac0d8..bed3a2c60ac 100644 --- a/addons/web_tour/static/tests/tour_manager_tests.js +++ b/addons/web_tour/static/tests/tour_manager_tests.js @@ -79,8 +79,7 @@ odoo.define('web_tour.tour_manager_tests', async function (require) { function onShowEffect(params) { assert.deepEqual(params, { fadeout: "medium", - message: "Good job! You went through all steps of this tour.", - messageIsHtml: true, + message: owl.markup("Good job! You went through all steps of this tour."), type: "rainbow_man" }); } diff --git a/odoo/addons/base/static/tests/base_settings_tests.js b/odoo/addons/base/static/tests/base_settings_tests.js index 1aad1695bd2..270bb6ee6e7 100644 --- a/odoo/addons/base/static/tests/base_settings_tests.js +++ b/odoo/addons/base/static/tests/base_settings_tests.js @@ -7,10 +7,11 @@ var view_registry = require('web.view_registry'); var createView = testUtils.createView; var BaseSettingsView = view_registry.get('base_settings'); -const { legacyExtraNextTick } = require("@web/../tests/helpers/utils"); +const { getFixture, legacyExtraNextTick } = require("@web/../tests/helpers/utils"); const { createWebClient, doAction } = require("@web/../tests/webclient/helpers"); let serverData; +let target; QUnit.module('base_settings_tests', { beforeEach: function () { this.data = { @@ -26,6 +27,7 @@ QUnit.module('base_settings_tests', { } }; serverData = { models: this.data }; + target = getFixture(); } }, function () { @@ -137,11 +139,11 @@ QUnit.module('base_settings_tests', { const webClient = await createWebClient({ serverData, mockRPC }); await doAction(webClient, 1); - await testUtils.dom.click($(webClient.el).find('button[name="4"]')); + await testUtils.dom.click($(target).find('button[name="4"]')); await legacyExtraNextTick(); await testUtils.dom.click($(".o_control_panel .breadcrumb-item a")); await legacyExtraNextTick(); - assert.hasClass($(webClient.el).find(".o_form_view"), "o_form_editable"); + assert.hasClass($(target).find(".o_form_view"), "o_form_editable"); assert.verifySteps([ "load_views", // initial setting action "onchange", // this is a setting view => create new record @@ -222,44 +224,44 @@ QUnit.module('base_settings_tests', { await doAction(webClient, 1); assert.containsNone( - webClient, + target, ".o_field_boolean input:checked", "checkbox should not be checked" ); - await testUtils.dom.click($(webClient.el).find("input[type='checkbox']")); + await testUtils.dom.click($(target).find("input[type='checkbox']")); assert.containsOnce( - webClient, + target, ".o_field_boolean input:checked", "checkbox should be checked" ); - await testUtils.dom.click($(webClient.el).find('button[name="4"]')); + await testUtils.dom.click($(target).find('button[name="4"]')); await legacyExtraNextTick(); assert.containsOnce(document.body, ".modal", "should open a warning dialog"); await testUtils.dom.click($(".modal button:contains(Discard)")); await legacyExtraNextTick(); - assert.containsOnce(webClient, ".o_list_view", "should be open list view"); + assert.containsOnce(target, ".o_list_view", "should be open list view"); await testUtils.dom.click($(".o_control_panel .breadcrumb-item a")); await legacyExtraNextTick(); assert.containsNone( - webClient, + target, ".o_field_boolean input:checked", "checkbox should not be checked" ); - await testUtils.dom.click($(webClient.el).find("input[type='checkbox']")); - await testUtils.dom.click($(webClient.el).find('button[name="4"]')); + await testUtils.dom.click($(target).find("input[type='checkbox']")); + await testUtils.dom.click($(target).find('button[name="4"]')); await legacyExtraNextTick(); assert.containsOnce(document.body, ".modal", "should open a warning dialog"); await testUtils.dom.click($(".modal button:contains(Stay Here)")); await legacyExtraNextTick(); - assert.containsOnce(webClient, ".o_form_view", "should be remain on form view"); + assert.containsOnce(target, ".o_form_view", "should be remain on form view"); - await testUtils.dom.click($(webClient.el).find("button[name='execute']")); + await testUtils.dom.click($(target).find("button[name='execute']")); await legacyExtraNextTick(); assert.containsNone( document.body, @@ -267,8 +269,8 @@ QUnit.module('base_settings_tests', { "should not open a warning dialog" ); - await testUtils.dom.click($(webClient.el).find("input[type='checkbox']")); - await testUtils.dom.click($(webClient.el).find("button[name='cancel']")); + await testUtils.dom.click($(target).find("input[type='checkbox']")); + await testUtils.dom.click($(target).find("button[name='cancel']")); await legacyExtraNextTick(); assert.containsNone( document.body, @@ -449,19 +451,19 @@ QUnit.module('base_settings_tests', { const webClient = await createWebClient({ serverData, mockRPC }); await doAction(webClient, 1); - assert.strictEqual($(webClient.el).find(".breadcrumb").text(), "First action"); + assert.strictEqual($(target).find(".breadcrumb").text(), "First action"); await doAction(webClient, 2); assert.strictEqual( - $(webClient.el).find(".breadcrumb").text(), + $(target).find(".breadcrumb").text(), "First actionNew" ); loadViewsDef = testUtils.makeTestPromise(); - await testUtils.dom.click($(webClient.el).find('button[name="3"]')); + await testUtils.dom.click($(target).find('button[name="3"]')); await legacyExtraNextTick(); assert.strictEqual( - $(webClient.el).find(".breadcrumb").text(), + $(target).find(".breadcrumb").text(), "First actionNew" ); @@ -469,7 +471,7 @@ QUnit.module('base_settings_tests', { await testUtils.nextTick(); await legacyExtraNextTick(); assert.strictEqual( - $(webClient.el).find(".breadcrumb").text(), + $(target).find(".breadcrumb").text(), "First actionNewOther action" ); } @@ -581,19 +583,19 @@ QUnit.module('base_settings_tests', { await doAction(webClient, 1); assert.containsNone( - webClient, + target, ".o_field_boolean input:checked", "checkbox should not be checked" ); - await testUtils.dom.click($(webClient.el).find('input[type="checkbox"]')); + await testUtils.dom.click($(target).find('input[type="checkbox"]')); assert.containsOnce( - webClient, + target, ".o_field_boolean input:checked", "checkbox should be checked" ); - await testUtils.dom.click($(webClient.el).find('button[name="4"]')); + await testUtils.dom.click($(target).find('button[name="4"]')); assert.containsOnce(document.body, ".modal", "should open a warning dialog"); await testUtils.dom.click($(".modal-footer .btn-primary"));
    +