diff --git a/addons/web/static/src/js/core/utils.js b/addons/web/static/src/js/core/utils.js index 15f3f973f24..d6165d0a841 100644 --- a/addons/web/static/src/js/core/utils.js +++ b/addons/web/static/src/js/core/utils.js @@ -897,6 +897,7 @@ var utils = { * * @param {Object} obj * @param {string} patchName + * @returns {Object} the removed patch */ unpatch: function (obj, patchName) { const objDesc = patchMap.get(obj); @@ -915,11 +916,16 @@ var utils = { } // Re-apply the patches except the one to remove. + let removedPatch; for (const patchDesc of objDesc.patches) { if (patchDesc.name !== patchName) { utils.patch(obj, patchDesc.name, patchDesc.patch); + } else { + removedPatch = patchDesc.patch; } } + + return removedPatch; }, /** * @param {any} node diff --git a/addons/web/static/tests/core/patch_tests.js b/addons/web/static/tests/core/patch_tests.js index d1b8a340fe5..4668bdf217c 100644 --- a/addons/web/static/tests/core/patch_tests.js +++ b/addons/web/static/tests/core/patch_tests.js @@ -290,6 +290,52 @@ QUnit.module('core', {}, function () { }); }); + QUnit.test('unpatch and re-patch', async function (assert) { + assert.expect(13); + + const BaseClass = makeBaseClass(assert); + + patch(BaseClass.prototype, 'patch', { + setup() { + this._super(); + assert.step('patch.setup'); + }, + fn() { + this._super(); + assert.step('patch.fn'); + }, + }); + + (new BaseClass()).fn(); + + assert.verifySteps([ + 'base.setup', + 'patch.setup', + 'base.fn', + 'patch.fn', + ]); + + const p = unpatch(BaseClass.prototype, 'patch'); + + (new BaseClass()).fn(); + + assert.verifySteps([ + 'base.setup', + 'base.fn', + ]); + + patch(BaseClass.prototype, 'patch', p); + + (new BaseClass()).fn(); + + assert.verifySteps([ + 'base.setup', + 'patch.setup', + 'base.fn', + 'patch.fn', + ]); + }); + QUnit.test('patch for specialization', async function (assert) { assert.expect(1); diff --git a/doc/reference/javascript_reference.rst b/doc/reference/javascript_reference.rst index eaa8c927dcf..c0d69668ca1 100644 --- a/doc/reference/javascript_reference.rst +++ b/doc/reference/javascript_reference.rst @@ -2811,10 +2811,14 @@ Therefore, patching a component constructor is always possible: Removing a patch ----------------- -We can also remove the patch by calling the ``unpatch`` function. This is really -only useful for tests. +In tests, it is sometimes useful to remove a patch for a specific test, for instance, +to remove a patch applied when another addon is installed, which overrides the behavior +implemented in the addon where the test is defined. The function ``unpatch`` exists for +that purpose. It returns the removed patch, such that it can be re-applied at the end +of the test. .. code-block:: javascript - unpatch(object, "patch name"); - + const p = unpatch(object, "patch name"); + // test stuff here + patch(object, "patch name", p);